Promiseを用いて、非同期関数を同期的に動かす

Promiseを用いて、JavaScriptの非同期関数を同期的に動かします。
本記事は、記事「setTimeoutを用いて、非同期関数を同期的に動かす」の続きになります。

基本はsetTimeoutですが、setTimeoutを利用すると「見た目」が複雑になります。
setTimeoutにPromiseを追加することで、「見た目」がシンプルになります。

(注)「見た目」が複雑
プログラムのネスト(nest、入れ子)が多段階になり、ネストが深くなった状態。
プログラムの可読性が低い状態。

(注)「見た目」がシンプル
プログラムのネストが浅くフラットな状態。プログラムの可読性が高い。

 

 

同期的と非同期的

同期的に動く

プログラムが「同期的に動く」とは、プログラムの上から下に向かって、順番に処理が進むことです。

 

プログラム1

ボタンをクリックすると、関数A()、B()、C() を実行するプログラム。

 

プログラム1で、上から順番に処理が進んだ場合、同期的に動いたことになります。

  1.  A()が動く
  2.  B()が動く
  3.  C()が動く

 

 

非同期的に動く

プログラムが「非同期的に動く」とは、「同期的には動かない」ことです。

プログラム1で、同期的に動かなかった場合、非同期的に動いたことになります。

例えば、

  1.  C()が動く
  2.  A()が動く
  3.  B()が動く

 

 

同期関数と非同期関数

同期関数

同期関数は同期的に動きます(同期処理)。

 

プログラム2
ボタンをクリックすると、テキストエリアに数字を表示するプログラム。

 

プログラム2は、上から下に向かって順番に1行ずつ処理が進みます。

  1.  テキストエリアに「10」と表示
  2.  テキストエリアに「20」と表示
  3.  テキストエリアに「30」と表示
  4.  テキストエリアに「40」と表示

 

非同期関数

非同期関数は非同期的に動きます(非同期処理)。

 

例えば、ボタンをクリックすると、次の動きをするプログラムを作りたいとします。

仕様A

  1.  テキストエリアを、1秒かけて非表示にする(スライドアップ)
  2.  テキストエリアに、「10」と表示
  3.  テキストエリアを、1秒かけて表示する(スライドダウン)
  4.  テキストエリアに、「20」と表示

 

仕様Aを、単純にプログラムにすると、以下となります。

プログラム3

 

(注)slideUp(1000)
スライドアップ(slideUP)は、要素を上方向にスライドして隠します。
数字の1000は、デュレーション(duration)で、スライド開始から完了までの時間です。
単位はm秒で、1000と書けば、1000m秒すなわち1秒となります。

(注)slideDown(1000)
スライドダウン(slideDown)は、隠れている要素を下方向にスライドして表示します。
数字の1000は、デュレーション(duration)です。

 

プログラム3の、slideUp()、slideDown()は非同期関数です。
そのため、プログラム3は非同期的に動きます。

実際に動作させると、以下の順に動きます。

  1.  テキストエリアに、「10」と表示
  2.  テキストエリアに、「20」と表示
  3.  テキストエリアを、1秒かけて非表示にする(スライドアップ)
  4.  テキストエリアを、1秒かけて表示する(スライドダウン)

 

この動きは、意図した動き(仕様A)になっていません。

仕様A通りに動くプログラムを作るには、非同期関数を同期的に動かす必要があります。

 

 

非同期関数を同期的に動かす

 

setTimeoutを用いた同期処理

 

setTimeoutを用いることで、非同期関数を同期的に動かすことができます。

 

プログラム4

 

プログラム4の説明

非同期関数A()の処理時間がduration_Aの場合、setTimeoutを用いて、同期関数B()をduration_A後に動かしています。

1. 非同期関数A()が動き始め、duration_A後に動き終わる
2. 同期関数B()が動く

setTimeoutを用いることで、A()が動き、その後にB()が動いています。

(注)詳細は、記事「setTimeoutを用いて、非同期関数を同期的に動かす」をご参照ください。

 

仕様Aを、setTimeoutを用いたプログラムにします(プログラム5)。

プログラム5

 

 

プログラムの説明

グローバル変数
・duration_A: スライドアップのデュレーションです。2秒に設定。
・duration_C: スライドダウンのデュレーションです。2秒に設定。

 

ボタンをクリックすると、以下の順で、同期的にプログラムが動きます。

  1.  13行目: A()が動き始め、2秒後に動き終わる
  2.  15行目: B()が動く
  3.  16行目: C()が動き始め、2秒後に動き終わる
  4.  18行目: D()が動く

 

 

Promiseを用いた同期処理

基本的には、上述した「setTimeoutを用いた同期処理」です。
それに、DeferredやPromiseを追加することで、「ネストが深くなる」難点を回避します。

 

プログラムの変形

非同期関数A()の次の処理(ここでは、B())を、以下の手順で変形します。

機能: テキストエリアに、「10」と表示する

 

まず、setTimeoutを用いて、同期処理に対応するよう変形します。

機能: duration_A後に、テキストエリアに「10」と表示する

 

次に、DeferredやPromiseを追加します(2、5、7行目)。

機能:

  • 2行目: deferを生成
  • 7行目: promiseを返す(promiseの状態は「pending」)
  • 4行目: duration_A後、テキストエリアに「10」と表示
  • 5行目: duration_A後、promiseの状態を「resolve」に変更

 

 

プログラム6

プログラム6は、以下の流れで動作します。

  1.  1行目: A() が動作
  2.  2行目: B() を呼出し、B() の返り値であるpromise(状態: pending)を保持
  3.  3行目: .then(promiseの状態が「resolve」に変化したら)、C()を実行

 

 

duration_A後に実行されるのは、以下となります。

  •  テキストエリアに「10」と表示
  •  C()

 

仕様Aを、Promiseを用いたプログラムにします(プログラム7)。

プログラム7

プログラムの説明

  • 14-16行目: Promiseによる連結

 

.then() は必ずPromiseを返します。

そのため、.then()で返したPromiseは以下のように連結できます。

 

また、以下のように表記を簡略化できます。

 

スポンサーリンク

コメントを残す

メールアドレスが公開されることはありません。