0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Promise.prototype.thenはいつ実行されるのか

Posted at

Promise、then、setTimeoutについて理解があいまいだったのでまとめてみました。

thenはいつ実行される?

いきなりですが、以下のコードの実行順序について考えてみます。

console.log("A");
new Promise((resolve, reject) => {
  resolve();
  console.log("B");
}).then(() => console.log("C"));
console.log("D");

上記を実行するとABDCの順で出力されます。
まずA出力され、次にPromiseのコンストラクタに渡された関数内のresolve()が実行され、Bが出力されます。(Promiseは非同期処理を扱うためのものですが、Promiseのコンストラクタに渡された関数は同期的に実行されます)そして、コンストラクタから返されたfulfilledの状態のPromiseオブジェクトで.thenが呼ばれます。すでにresolve()が実行されているので、Cがすぐに出力されそうですが、実際はDの後に出力されます。なぜすぐに実行されないのでしょうか?それにはJavaScriptの非同期処理について理解する必要があります。

JavaScriptの非同期処理

JavaScriptは基本的にシングルスレッドで実行さるので、処理が同時に実行されることはありません。同時に実行されているように見えるのは、イベントループにより実行を遅らせたり分割したりするからです。イベントループとは以下のような繰り返しです。

  1. キューからタスクを取り出す(なければ待機)
  2. 実行する

キューには2種類ありタスクキューマイクロタスクキューがあります。setTimoutを使うと処理はタスクキューに追加されます。Promiseのthenを使うと処理はマイクロタスクキューに追加されます。実行される順番は以下のようになります。

  1. タスクキューからタスクを取り出し実行する
  2. マイクロタスクキューが空になるまでマイクロタスクを実行する(その間新しく追加されたとしても)
  3. 必要であればレンダリングする

非同期処理の例

もう一度先ほどのコードに戻ります。

console.log("A");
new Promise((resolve, reject) => {
  resolve();
  console.log("B");
}).then(() => console.log("C")); // マイクロタスキューに追加される
console.log("D");

まず、プログラムがタスクとして読み込まれ、同期的にABDに出力されます。最後に非同期でCが出力されるのは、thenの処理はマイクロタスクとして現在のタスクが完了した後に実行されるからです。

Promiseの部分は以下のように書き換えることができます。

Promise.resolve().then(() => console.log("Microtask"));

そして上記のコードはマイクロタスキューに追加しているだけなので、さらに以下のように書き換えることができます。

queueMicrotask(() => console.log("Microtask"));

queueMicrotask()setTimeout()のようなタスクよりも先に実行されます。
実行順序は以下の通りです。

console.log("A");
setTimeout(() => console.log("Task"));
queueMicrotask(() => console.log("Microtask1"));
queueMicrotask(() => {
  console.log("Microtask2");
  queueMicrotask(() => console.log("Microtask3"));
});
console.log("B");
結果
A
B
Microtask1
Microtask2
Microtask3
Task

まず、同期的にABが出力されます。そしてレンダリングや次のタスクの前にマイクロタスクが実行されるのでMicrotask1Microtask2が出力されます。その後マイクロタスク内でさらにマイクロタスクを追加しています。途中で追加されたマイクロタスクも実行されMicrotask3と出力されます。差後にsetTimeoutでタスキューに追加されたタスクが実行されTaskと出力されます。この順番はマイクロタスクの処理にどれほど時間がかかったとしても変わりません。マイクロタスクが追加され続けると画面の更新処理が行えず、画面が固まってしまうので気を付ける必要があります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?