JavaScriptの非同期処理管理オブジェクトPromiseを使用してアニメーションを連続で行うサンプルを作成しました。
Promiseはほとんど使ったことがなかったのですが、今回requestAnimationFrameメソッドによるアニメーション関数をつなぐため使用しました。アニメーション関数から直接次の関数を呼んだりsetTimeoutメソッドを使ってつなぐこともできますが、Promiseを使うとアニメーション関数群の依存関係をなくすことができたり関数の呼び出しを一か所にまとめて記述できるので、保守性や可読性の向上が期待できます。
Promiseについて参考にしたサイト
アニメーションについて参考にしたサイト
サンプル1
See the Pen Promise anim by senmyou (@senmyou) on CodePen.
サンプル2
See the Pen Promise anim by senmyou (@senmyou) on CodePen.
サンプルについて
- Arrayオブジェクトのreduceメソッドを使ってPromiseオブジェクトをつなぐ方法は、JavaScript Promiseの本の4.8. Promiseによる逐次処理やMDNの合成 (Composition)で解説されています。
- requestAnimationFrameメソッドに渡すコールバック関数は、引数でタイムスタンプ(ミリ秒)を受け取ります。このタイムスタンプについてAPIのドキュメントを見てもわからないことがあり実験してみました。結果的にタイムスタンプの説明に次の2点を追記できると思います。
- タイムスタンプはhtmlのページが読み込まれてから経過した時間(ミリ秒)を示す
- タイムスタンプをプログラムから0に戻すことはできない
また、これらのことからタイムスタンプはある基準点との差分をとって使うことになると思います。サンプルではアニメーションの基準(開始)点として初回のrequestAnimationFrameメソッドのタイムスタンプをstartTimeに保存して、毎回現在のタイムスタンプと差分をとって進捗度(progress)の算出に使用しています。
Promise備忘録
Promiseについての個人的な備忘録です。
- Promiseのコンストラクタはexecutorと呼ばれる関数を引数にとる。
- executorの引数にresolve関数とreject関数が渡される。基本的にexecutorで非同期処理を行い、そのなかでresolve関数またはreject関数を実行してPromiseオブジェクトの状態を変更させる。これが次段のコールバック関数の実行トリガになる。
- executorはすぐに(コンストラクタがPromiseオブジェクトを返すよりも前に)実行される。これに対しthenメソッドへ渡すコールバック関数はすぐには実行されない。
- thenメソッドはコールバック関数を登録してすぐに返る。この戻り値は新しいPromiseオブジェクトになるため、戻り値からthenメソッドを使うことでメソッドチェーンを作成することができる。
例:promiseA.then(callbackA).then(callbackB).then(callbackC) - Promiseインターフェイスは作成時点では分からなくてもよい値へのプロキシ(代理)で、Promiseオブジェクトをthenメソッドによりハンドラ(コールバック関数)に関連付けることができる。
Promiseオブジェクトをconsole.logで表示
- PromiseオブジェクトはPromiseStatusプロパティとPromiseValueプロパティを持つ
- コールバック関数がresolve()を実行すると対応するPromiseオブジェクトのPromiseStatusプロパティが”resolved”になる
- これらのプロパティはプログラムから参照できないので実際は気にしなくていい
- thenメソッドで登録したコールバック関数が値を返した場合は、暗黙的にPromise.resolve(値)によりPromiseでラップされる(値を返さない場合はundefinedをラップ)。続いて次段のコールバック関数が呼び出される。このとき引数に値が渡される。
- thenメソッドで登録したコールバック関数がPromiseオブジェクトを返した場合は、そのPromiseオブジェクトがFulfilledまたはRejectedになるまで次段のコールバック関数は呼び出されない。この仕組みを利用して非同期処理の完了を待って次のコールバック関数を呼び出すことができる。
(個人的なイメージ:コールバックから返されたPromiseオブジェクトがexecutorのresolve/reject関数によりFulfilled/Rejectedに状態を変えると、その状態がthenメソッドで生成したPromiseオブジェクトに伝搬し、関連付けられたコールバック関数が呼び出される)