🐕

es6-promiseをコードリーディングしてPromiseの挙動を理解する

2024/12/31に公開

最初に

es6-promiseはPromiseの基本的な仕組みを自前で実装して、以前のJavaScriptでも動くようになっています。
es6-promiseのコードから主要な部分をコードリーディングしてPromiseの仕組みを理解していきます。

https://github.com/stefanpenner/es6-promise

コードの理解

状態管理

Promiseは下記のように3つの状態を管理しています。
早期returnをして、Promiseの状態がpendingの時だけ状態を変化するようにしています。

  1. PENDING: undefined(初期状態)
  2. FUFILLED: 1(resolveが呼ばれ成功となった状態)
  3. REJECTED: 2(rejectが呼ばれ失敗となった状態)

https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/-internal.js#L17-L19

下記で状態の初期設定をしています。
https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/promise.js#L137

ユーザー任意のコードを実行後にresolveが呼ばれた場合は下記が実行されます。
pendingではない場合は、早期returnするようにしており、pendingの場合は状態をFULFILLEDに変更しています。

https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/-internal.js#L114-L123

ユーザーが任意の実装をして、rejectが呼ばれた場合は下記が実行されます。
pendingではない場合は、早期returnするようにしており、pendingの場合は状態をREJECTEDに変更しています。

https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/-internal.js#L125-L131

キューの仕組み

キューは非同期的にコールバック(thenやcatchメソッド)を実行するための実装です。
setTimeoutを利用してタスクキューに追加して、非同期的にflushを実行しています。
https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/asap.js#L75-L80

flushでは、キューに登録されたコールバックを順番に取り出して実行します。
https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/asap.js#L82-L95

setTimeout以外にも環境によって非同期実行をする方法を分けています。
https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/asap.js#L107-L119

fulfillrejectが呼ばれた後にhandlersにコールバックが登録されていれば、asapが実行されます。
asapメソッドはコールバックとその引数をキューに登録して、初回の登録時にscheduleFlushを呼び出します(非同期処理をタスクキューに登録)。上述のscheduleFlushのコードのように非同期でコールバックを処理する準備をしています。

https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/asap.js#L5-L19

thenメソッド

then が呼ばれると、状態がpendingの場合は成功時と失敗時のコールバックがhandlersキュー(非同期処理が登録されるコールバックのリスト)に登録されます。
また、状態がresolvedまたは rejectedの場合は登録されたコールバックがinvokeCallbackによって非同期に実行されます。
FIFOで複数の.thenチェーンが登録されている場合、それらを登録順に実行するようにしています。

https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/then.js#L13-L32

Promiseをreturnすることでコールバックチェーンが続けられるようにして、何度もthenを呼び出せるようにしています。

https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/then.js#L31

まとめ

es6-promiseの実装を参考にPromiseの挙動を確認していきました。
OSSを見て理解を深めていくことは、土台となる技術の仕組みの理解を進めることができ、良いと感じました。

Discussion