Promiseオブジェクト
概要
Promise オブジェクトは JavaScript の非同期操作ソリューションであり、非同期操作用の統一インターフェイスを提供します。これはプロキシとして機能し、非同期操作とコールバック関数の間の仲介者として機能するため、非同期操作は同期操作用のインターフェイスを持ちます。 Promise を使用すると、コールバック関数をレイヤーごとにネストすることなく、同期操作プロセスを作成するのと同じように非同期操作を作成できます。
この章は Promise オブジェクトの簡単な紹介にすぎないことに注意してください。後続のチュートリアルとの重複を避けるため、より完全な紹介については、[「ES6 標準の紹介」](http://es6. ruanyifeng.com/) /#docs/promise) の章。
まず、Promise はオブジェクトでありコンストラクターです。
関数 f1(解決、拒否) {
// 非同期コード...
}
var p1 = 新しい Promise(f1);
上記のコードでは、Promise
コンストラクターはコールバック関数 f1
をパラメーターとして受け取り、f1
には非同期操作のコードが含まれています。そして、返された p1
は Promise インスタンスです。
Promise の設計上の考え方は、すべての非同期タスクが Promise インスタンスを返すというものです。 Promise インスタンスには「then」メソッドがあり、次のステップのコールバック関数を指定するために使用されます。
var p1 = 新しい Promise(f1);
p1.then(f2);
上記のコードでは、「f1」の非同期操作が完了した後、「f2」が実行されます。
従来の記述では、「f1(f2)」を記述するなど、コールバック関数として「f2」を「f1」に渡す必要がある場合があります。非同期操作が完了した後、「f1」内で「f2」が呼び出されます。 Promise により f1
と f2
がチェーンされます。読みやすさが向上するだけでなく、マルチレベルのネストされたコールバック関数に特に便利です。
// 伝統的な書き方
ステップ1(関数(値1) {
ステップ2(値1, 関数(値2) {
ステップ3(値2, 関数(値3) {
ステップ4(値3, 関数(値4) {
// ...
});
});
});
});
// Promiseの書き方
(新しい約束(step1))
.then(ステップ2)
.then(ステップ3)
.then(ステップ4);
上記のコードからわかるように、Promises を使用すると、プログラム フローが非常に明確になり、読みやすくなります。理解を容易にするために、上記のコードの「Promise」インスタンスの生成形式は簡略化されていることに注意してください。実際の構文については、以下を参照してください。
一般に、コールバック関数を記述する従来の方法では、コードが混乱し、下方向ではなく水平方向に発展してしまいます。 Promise はこの問題を解決し、非同期プロセスを同期プロセスとして記述できるようにします。
Promise はもともとコミュニティによって提案されたアイデアにすぎず、一部の関数ライブラリがこの機能の実装を主導しました。 ECMAScript 6 はこれを言語標準に組み込み、JavaScript は現在 Promise オブジェクトをネイティブにサポートしています。
Promise オブジェクトの状態
Promise オブジェクトは、独自の状態を通じて非同期操作を制御します。 Promise インスタンスには 3 つの状態があります。
- 非同期操作が保留中 (保留中)
- 非同期操作が実行されました (実行されました)
- 非同期操作が失敗しました (拒否されました)
上記3つの状態のうち、「履行」と「拒否」を総称して「解決」(確定)と呼ぶ。
これら 3 つの状態を変更するには 2 つの方法しかありません。
- 「未完成」から「成功」へ
- 「未完了」から「失敗」へ
一度変化した状態は固定され、新たな状態変化はありません。英語で「約束」を意味するプロミスという名前の由来でもあります。これは、Promise インスタンスの状態変更は 1 回だけであることも意味します。
したがって、Promise の最終結果は 2 つだけです。
- 非同期操作は成功し、Promise インスタンスは値 (value) を返し、ステータスは「fulfilled」になります。
- 非同期操作は失敗し、Promise インスタンスはエラーをスローし、ステータスが「拒否」に変わります。
Promise コンストラクター
JavaScript は、Promise インスタンスを生成するためのネイティブ Promise コンストラクターを提供します。
varpromise = new Promise(function (解決、拒否) {
// ...
if (/* 非同期操作が成功しました */){
解決(値);
} else { /* 非同期操作が失敗しました */
拒否(新しいエラー());
}
});
上記のコードでは、Promise
コンストラクターはパラメーターとして関数を受け取り、関数の 2 つのパラメーターは resolve
と reject
です。これらは JavaScript エンジンによって提供される 2 つの関数であり、自分で実装する必要はありません。
resolve
関数の役割は、Promise
インスタンスのステータスを「未完了」から「成功」に変更することです (つまり、「保留中」から「完了」に)。非同期操作が成功したときに呼び出されます。 、非同期操作は、結果がパラメータとして渡されます。 reject
関数の機能は、Promise
インスタンスのステータスを「未完了」から「失敗」に変更することです (つまり、「保留中」から「拒否済み」に)。非同期操作が失敗した場合に呼び出されます。発生したエラーがパラメータとして渡される非同期操作を報告します。
以下に例を示します。
関数のタイムアウト(ミリ秒) {
return new Promise((解決、拒否) => {
setTimeout(resolve, ms, 'done');
});
}
タイムアウト(100)
上記のコードでは、「timeout(100)」は Promise インスタンスを返します。 100 ミリ秒後、インスタンスのステータスは「fulfilled」に変わります。
Promise.prototype.then()
Promise インスタンスの「then」メソッドは、コールバック関数を追加するために使用されます。
then
メソッドは 2 つのコールバック関数を受け入れることができます。1 つは非同期操作が成功したときのコールバック関数 (fulfilled
状態に変化)、2 つ目は非同期操作が失敗したときのコールバック関数 (rejected
に変化) です。 (このパラメータは省略可能です)。状態が変化すると、対応するコールバック関数が呼び出されます。
var p1 = new Promise(function (解決、拒否) {
解決('成功');
});
p1.then(console.log、console.error);
// "成功"
var p2 = new Promise(function (解決、拒否) {
拒否(新しいエラー('失敗'));
});
p2.then(console.log、console.error);
// エラー: 失敗しました
上記のコードでは、p1
と p2
はどちらも Promise インスタンスであり、それらの then
メソッドは 2 つのコールバック関数 (成功時のコールバック関数 console.log
と失敗時のコールバック関数 console.error
) をバインドします。 . (省略可能)。 p1
のステータスは成功に変わり、p2
のステータスは失敗に変わります。対応するコールバック関数は、非同期操作によって返された値を受け取り、それをコンソールに出力します。
「then」メソッドは連鎖させることができます。
p1
.then(ステップ1)
.then(ステップ2)
.then(ステップ3)
。それから(
コンソール.ログ、
コンソールエラー
);
上記のコードでは、「p1」の後に「then」が 4 つあります。これは、コールバック関数が 4 つ連続していることを意味します。前のステップのステータスが「達成」に変わる限り、次のコールバック関数が順番に実行されます。
最後の「then」メソッド、コールバック関数は「console.log」と「console.error」ですが、使用法には重要な違いがあります。 「console.log」は「step3」の戻り値のみを表示しますが、「console.error」は「p1」、「step1」、「step2」、「step3」のいずれかで発生したエラーを表示できます。たとえば、「step1」のステータスが「rejected」に変更された場合、「step2」と「step3」は実行されません(「resolved」のコールバック関数であるため)。 Promise は検索を開始し、最初のコールバック関数は「rejected」になります。これは、上記のコードの「console.error」です。これは、Promise オブジェクトによって報告されるエラーが推移的であることを意味します。
##then() の使用分析
Promise の使用法は、「then」メソッドを使用してコールバック関数を追加するという 1 つの文だけです。ただし、それぞれの書き方には微妙な違いがあります。次の 4 つの書き方の違いをご覧ください。
// 書き方その1
f1().then(関数() {
f2()を返します;
});
//書き方2
f1().then(関数() {
f2();
});
//書き方3
f1().then(f2());
// 書き方4
f1().then(f2);
説明の便宜上、以下の 4 つの記述メソッドはすべて、コールバック関数 f3
を接続するために then
メソッドを使用します。最初の方法で記述された f3
コールバック関数のパラメータは、f2
関数の実行結果です。
f1().then(関数() {
f2()を返します;
}).then(f3);
2 番目の方法の f3
コールバック関数のパラメータは undefine
です。
f1().then(関数() {
f2();
戻る;
}).then(f3);
3 番目のメソッドで記述された f3
コールバック関数のパラメータは、f2
関数によって返される関数の実行結果です。
f1().then(f2())
.then(f3);
メソッド 4 の作成とメソッドの作成との違いは 1 つだけです。それは、f2
が f1()
によって返された結果を受け取ることです。
f1().then(f2)
.then(f3);
例: 画像の読み込み
以下は、Promise を使用して画像の読み込みを完了する方法です。
var preloadImage = 関数 (パス) {
return new Promise(function (解決、拒否) {
var image = 新しい画像();
image.onload = 解決;
image.onerror = 拒否;
画像.src = パス;
});
};
上記のコードでは、「image」は画像オブジェクトのインスタンスです。これには 2 つのイベント リスニング プロパティがあり、「onload」プロパティは画像が正常に読み込まれた後に呼び出され、「onerror」プロパティは読み込みが失敗した後に呼び出されます。
上記のpreloadImage()
関数の使い方は以下の通りです。
preloadImage('https://example.com/my.jpg')
.then(関数 (e) { document.body.append(e.target) })
.then(function () { console.log('読み込み成功') })
上記のコードでは、画像が正常にロードされた後、onload
属性がイベント オブジェクトを返すため、最初の then()
メソッドのコールバック関数がこのイベント オブジェクトを受け取ります。このオブジェクトの「target」属性は、イメージがロードされた後に生成される DOM ノードです。
まとめ
Promise の利点は、コールバック関数を標準化されたチェーン記述メソッドに変換し、プログラムの流れが明確に見えることです。これには、複数の非同期操作を同時に実行し、そのステータスが変化するまで待機してからコールバック関数を実行するなど、多くの強力な機能を実装できるインターフェイスの完全なセットが備わっています。別の例としては、複数のコールバック関数でスローされたエラーを均一に指定することが挙げられます。加工方法など。
さらに、Promise には従来の書き込みにはないもう 1 つの利点があります。ステータスが変更されると、クエリを実行するたびにこのステータスを取得できるということです。これは、コールバック関数を Promise インスタンスに追加するたびに、関数が正しく実行されることを意味します。そのため、イベントや信号を見逃すことを心配する必要はありません。従来の方法で記述した場合、イベントをリッスンすることによってコールバック関数が実行されます。イベントが見逃されると、追加されたコールバック関数は実行されません。
Promise の欠点は、従来の記述よりも記述が難しく、コードを読んでも一目で理解するのが容易ではないことです。大量の「then」のみが表示されるため、「then」コールバック関数内のロジックを整理する必要があります。
マイクロタスク
Promise のコールバック関数は非同期タスクであり、同期タスクの後に実行されます。
new Promise(function (解決、拒否) {
解決(1);
}).then(console.log);
コンソール.ログ(2);
// 2
// 1
上記のコードは、最初に 2 を出力し、次に 1 を出力します。なぜなら、「console.log(2)」は同期タスクであり、「then」のコールバック関数は非同期タスクであり、同期タスクよりも後で実行する必要があるためです。
ただし、Promiseのコールバック関数は通常の非同期タスクではなく、マイクロタスクです。それらの違いは、通常のタスクは次のイベント ループに追加され、マイクロタスクは現在のイベント ループに追加されることです。これは、マイクロタスクは通常のタスクよりも早く実行する必要があることを意味します。
setTimeout(関数() {
コンソール.ログ(1);
}, 0);
new Promise(function (解決、拒否) {
解決(2);
}).then(console.log);
コンソール.ログ(3);
// 3
// 2
// 1
上記のコードの出力は「321」です。これは、「then」のコールバック関数の実行時間が「setTimeout(fn, 0)」よりも早いことを示しています。このイベント ループでは「then」が実行されるため、次のイベント ループの開始時に「setTimeout(fn, 0)」が実行されます。
参考リンク
- Sebastian Porto、非同期 JS: コールバック、リスナー、コントロール フロー ライブラリ、および Promises
- Rhys Brett-Bowen、[Promises/A+ - 実装による仕様の理解](http://modernjavascript.blogspot.com/2013/08/promisesa- Understanding-by-doing.html)
- Matt Podwysocki 氏、Amanda Silver 氏、「約束」を使用した JavaScript の非同期プログラミング
- Marc Harter、Promise A+ 実装
- ブライアン クリムト、JavaScript Promises の何がそんなに素晴らしいのですか?
- Jake Archibald、JavaScript の約束はそこにあり、また戻ってきます
- 高田幹人、7. 制御フロー、Mixu の Node ブック
作者: wangdoc
アドレス: https://wangdoc.com/
ライセンス: クリエイティブ・コモンズ 3.0