将来的にはECMAScript 6標準の一部となる注目すべきPromisesテクノロジーに関するHabréの記事がすでにありますが、これらの記事には、それらが非常に役立つ理由とその利点についての詳細な説明は含まれていません。 このギャップを埋めるために、私はこの記事を書くことにしました。
注意! この記事は完全なガイドではありません。 それはむしろ、有益で有用な技術のPRであり、肯定的な側面を示す誘惑です。 この記事は、他のいくつかの記事をまとめたもので、以下のリンクです。
Promiseとは何ですか?
- Promiseは、保留中の(および場合によっては非同期の)計算の結果のスタブとして使用されるオブジェクトです
- シリアル/パラレル非同期コードを同期として記述する方法
- ECMAScript 6標準の一部
重要な基本
- Promiseコンストラクターは、 Reveealing Constructor Patternの実装です
- 「静的」メソッド
- Promise.resolve(val)およびPromise.reject(val)
- Promise.allおよびPromise.race
- 「Thenable」オブジェクトの概念
- 一言で言えば-これは.then()および.catch()メソッドを持つオブジェクトです
ステータス約束
- 完了 -計算は成功しました
- 拒否 -計算中のエラー(任意)
- 保留中 -計算はまだ完了していません(実行も拒否もされていません)
- 確定 -計算は完了しました(方法は関係ありません)
例
だから、同期コードの一部を取ります
function foo() { var a = 'a'; a = a + 'b'; a = a + 'c'; return a; }
そして、それを外側の約束のように見せます:
function foo() { var a = 'a'; a = a + 'b'; a = a + 'c'; return Promise.resolve(a); }
次のステップは、計算の各ステップを分割することです。
function foo() { return Promise.resolve('a') .then(function(a){ return a + 'b'; }) .then(function(a){ return a + 'c'; }); }
これで、各ステップを非同期にできるようになり、すべての実行が引き続きシーケンシャルになります。
先に進み、手順の1つを「あたかもPromiseを返す非同期関数のように」置き換えます。
function getB(a){ return Promise.resolve(a + 'b'); } function foo() { return Promise.resolve('a') .then(function(a){ return getB(a); }) .then(function(a){ return a + 'c'; }); }
組み込みの機能により、そのとき(cb())エラー(新しいError()を投げる)または値(a + 'c'を返す)または次のPromiseに戻ることができます。
並行性
最初に非同期アクション1を実行し、次に2と3に並行して、次に4を実行する必要があると想像してください。
asyncAction1() .then(function(res1){ return Promise.all([async2(res1), async3(res1)]); }) .then(function(arr){
エラー処理
Promiseの素晴らしいところはエラー処理です。 エラーがどの段階でどの程度ネストされているかは問題ではありません。エラーが拒否されるか例外がスローされるかは関係ありません。これらすべてをキャッチして処理することも、先にスキップすることもできます。
asyncAction() .catch(function(rejection){
ここで、観察する必要があります。
var p1 = new Promise(...), p2 = new Promise(...) p3 = Promise.all([p1, p2]); p3.then(...).catch(...);
Catchは、p1、p2、p3、およびネストされた呼び出しで問題が発生したすべてをキャッチします(何がどのように関係なく)。これは非常に便利です。 反対側-catch()が存在しない場合、エラーは静かに飲み込まれます。 ただし、Qのようなライブラリは、通常、コンソールなどに表示できるキャッチされていないエラーのハンドラーを設定する機能を備えています。
アンチパターンに関する言葉
function anAsyncCall() { var promise = doSomethingAsync(); promise.then(function(){ somethingComplicated(); }); return promise; }
そして、手首を軽くたたくと、2番目のPromiseが失われました。 実際、.then()または.catch()を呼び出すたびに新しいPromiseが作成されるため、新しいPromiseを作成して古いPromiseを返すと、新しいPromiseは空中のどこかにハングアップし、計算の結果が誰にもわかりません。 戦う方法-新しいPromiseを返すだけです:
return promise.then(...);
便利な肉焼き
遅延実行
function delay(ms){ return new Promise(function(resolve){ setTimeout(resolve, ms); } };
使用例
delay(5000).then(…);
最も単純なタイムアウト
Promiseは一度しか決済できないため(残りは無視されます)、次のように書くことができます。
function timeout(promise, ms) { return new Promise(function (resolve, reject) { promise.then(resolve); setTimeout(function () { reject(new Error('Timeout')); }, ms); }); } timeout(asyncAction(), 5000).then(…).catch(…);
誰が最初に起きた-それとスリッパ。
わずかに改善されたタイムアウト
もう少し明白なタイムアウトの例は、「静的」関数Promise.race()を使用することです。
Promise.race([ asynchronousAction(), delay(5000).then(function () { throw new Error('Timed out'); }) ]) .then(function (text) { ... }) .catch(function (reason) { ... });
非同期に関する重要な注意
- ライブラリはそれぞれ実行プロセスを制御し、結果の配信方法を同期的または非同期的に管理します
- 同時に、Promises / A +仕様では、最後のモードを常に使用する必要があります-非同期
- したがって、promise.then()。Catch()コードなどの即時実行に常に依存することができ、コールバックの一部がすべてのプロセッサー時間を消費することを心配しません(もちろん予約あり)
約束がコールバックよりも優れている理由
- 彼らは標準の一部です-賢い人々は私たちの便宜のためにすべてを考えて開発しました
- 「実質的に」パフォーマンスに影響しません( http://thanpol.as/javascript/promises-a-performance-hits-you-should-be-aware-of )
- Promisesの実装を記述しない場合、コールバックでの実行の重要なフローを解決しようとします。できればエラー処理を使用してください。
- すべてのPromises / A +互換ライブラリは、互いのオブジェクトを受け入れることができます(AngularはQ / Native Promise / RSVPオブジェクトなどでうまく機能します)
- PromiseはDeferredよりも優れています。後者は2つの概念であり、これは確かに悪いことです。また、Reveinging Constructorパターンは、Promiseがこのコンストラクター内でのみ解決されることをほぼ保証します(まあ、あなたは自分自身に悪意があるピノキオ)
欠点
- 定期的なイベントには適していません(ただし、約束は書かれていません)
- ストリームには適していません(類似)
- ブラウザーでの現在の実装では、進行状況の監視が許可されていません(これらも標準に含まれる予定です)
JQueryノート
jQueryでの「Thennable」の実装は、標準のものとわずかに異なります。標準では、コールバックの引数は正確に1です。jQueryでは、引数の数が多く、ネイティブ実装でjQueryから取得したオブジェクトの使用を妨げません。 原則として、最初の引数以上に、jQueryからの何も必要ありません。 さらに、デザインがあります:
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
自己読み取りリンク