はじめに
JavaScriptのPromiseは理解するのが難しいことがあります。そこで、私がPromiseを理解する方法を書き留めたいと思います。
Promiseの理解
Promiseとは簡単に言うと:
「あなたが子供だと想像してください。お母さんが来週新しい携帯電話を買ってくれると約束しました。」
来週までその携帯電話を手に入れるかどうかはわかりません。お母さんは本当に新しい携帯電話を買ってくれるかもしれませんし、買ってくれないかもしれません。
それが約束です。約束には三つの状態があります。それらは:
- Pending(保留中):あなたはその携帯電話を手に入れるかどうかわからない
- Fulfilled(履行済み):お母さんは嬉しいので、あなたに新しい携帯電話を買ってくれます
- Rejected(拒否された):お母さんは悲しいので、携帯電話を買ってくれません
Promiseの作成
これをJavaScriptに置き換えてみましょう。
コード自体が非常に表現力豊かです。
以下は、Promiseの構文が通常どのように見えるかです:
Promiseの利用
Promiseができたので、それを利用してみましょう:
例を実行して結果を見てみましょう!
デモ: https://jsbin.com/nifocu/1/edit?js,console
Promiseの連鎖
Promiseは連鎖可能です。
例えば、あなたが子供として、友達に母が新しい携帯電話を買ってくれたら約束して、それを見せると言いました。
それは別の約束です。書いてみましょう!
注: 上記のコードは以下のように短縮できます:
Promiseを連鎖させてみましょう。あなたが子供として、willIGetNewPhone
の約束が終わった後でのみshowOff
の約束を開始できます。
このようにして、Promiseを連鎖させることができます。
約束は非同期です
約束は非同期です。約束を呼び出す前後にメッセージをログに記録してみましょう。
予想される出力の順序は何ですか?次のように予想するかもしれません:
1. before asking Mom
2. Hey friend, I have a new black Samsung phone.
3. after asking mom
しかし、実際の出力順序は次のとおりです:
1. before asking Mom
2. after asking mom
3. Hey friend, I have a new black Samsung phone.
お母さんの約束(新しい携帯電話)を待っている間、遊びをやめることはありません。それが非同期と呼ばれるものです:コードは結果を待たずに実行されます。約束の結果を待つ必要があるものは、.then
に入れられます。
以下はES5での完全な例です:
ES5, ES6/2015, ES7/NextにおけるPromises
ES5 – 大多数のブラウザ
デモコードはBluebird Promiseライブラリを含めることでES5環境(すべての主要ブラウザ + NodeJs)で動作します。これはES5がPromiseを標準でサポートしていないためです。もう一つ有名なPromiseライブラリはKris KowalによるQです。
ES6 / ES2015 – モダンブラウザ、NodeJs v6
デモコードはそのままで動作します。これはES6がPromiseをネイティブにサポートしているためです。さらに、ES6の機能を使うことで、アロー関数を用いてコードをさらに簡潔にし、const
とlet
を使用することができます。
以下はES6コードの完全な例です:
var
はすべてconst
に置き換えられており、function(resolve, reject)
は(resolve, reject) =>
と簡略化されています。これらの変更により、いくつかの利点が得られます。
ES7 – Async/Await
ES7ではasync
とawait
の構文が導入されました。これにより、非同期処理が.then
や.catch
なしで理解しやすくなります。
ES7の構文で例を書き換えると以下のようになります:
Promiseの使用とそのタイミング
なぜPromiseが必要なのでしょうか?Promiseが登場する前の世界はどのようなものだったのでしょうか?これらの質問に答える前に、基本から見直してみましょう。
通常の関数と非同期関数
これら二つの例を見てみましょう。どちらも二つの数値を加算します:一つは通常の関数を使って加算し、もう一つはリモートで加算します。
二つの数値を加算する通常の関数
二つの数値を加算する非同期関数
通常の関数で数値を加算すると、すぐに結果が得られます。しかし、リモート呼び出しを行って結果を得る場合、待ち時間が発生し、すぐに結果を得ることはできません。
サーバーがダウンしていたり、応答が遅かったりするため、結果が得られるかどうかわかりません。結果を待つ間、プロセス全体がブロックされることを望みません。
APIの呼び出し、ファイルのダウンロード、ファイルの読み取りなど、いくつかの一般的な非同期操作を実行します。
非同期呼び出しにプロミスを使用する必要はありません。プロミスの前は、コールバックを使用していました。コールバックは、結果を得たときに呼び出す関数です。前の例を修正して、コールバックを受け入れるようにしましょう。
後続の非同期アクション
数値を1つずつ加算する代わりに、3回加算したいと思います。通常の関数では、次のようにします:
これがコールバックを使った場合の見え方です:
デモ: https://jsbin.com/barimo/edit?html,js,console
この構文は、深くネストされたコールバックのため、ユーザーフレンドリーではありません。
深くネストされたコールバックを避ける
プロミスは、深くネストされたコールバックを避けるのに役立ちます。同じ例をプロミスバージョンで見てみましょう:
Promiseを使うことで、.then
でコールバックを平坦化できます。ある意味、コールバックのネストがないため、よりクリーンに見えます。ES7のasync
構文を使うと、この例をさらに改善できます。
オブザーバブル
Promiseに落ち着く前に、非同期データを扱うのに役立つオブザーバブル
という概念が登場しました。
同じデモをオブザーバブルで書いてみましょう。この例では、オブザーバブルにRxJSを使用します。
オブザーバブルはもっと興味深いことができます。例えば、delay
関数を3秒
遅らせたり、特定の回数だけ再試行できるようにすることができます。
RxJsに関する私の投稿の一つをこちらで読むことができます。
結論
コールバックとプロミスに慣れ親しむことは重要です。それらを理解し、使いこなしてください。オブザーバブルについてはまだ心配しないでください。状況に応じて、これら三つすべてがあなたの開発に関与する可能性があります。
Source:
https://www.digitalocean.com/community/tutorials/understanding-javascript-promises