ES 6-async+await同期/非同期方案を読み返す
6387 ワード
非同期プログラミングは常に
本文力は最も簡明な方法で
非同期プログラミングのいくつかのシーン
まず、一般的な問題から開始します.1つの
私たちはこの問題について、クローズドまたは
非同期が均一に発生しない場合、非同期キューに登録されている順序は乱れている.
変调
非同期化案
考えてみてください.もしいくつかのデータをサーバに送信するなら、前のバッチが成功した(すなわちサーバが成功した応答を返した)だけが次のデータの送信を開始します.そうでなければ送信を終了します.これは典型的な「forサイクルにおける相互依存非同期動作」の例である.
このような「シリアル」の非同期は、本質的には同期として扱うことができることは明らかである.乱序の非同期と比べて、より多くの時間がかかった.道理から言えば、プログラムは非同期で実行したいです.つまり、ブロックをスキップするために、より少ない時間で費用がかかります.逆に、一連の非同期の「シリアル」が必要なら、どうやってプログラミングすればいいですか?
この「シリアル」非同期については、
これは非同期問題の同期化案である.このスキームに関しては、
async+await「外異内同」
最初にこのAPIに接触した時、煩雑な文書を見ていて、主に非同期化を解決するために
実はそうではないです.上記の例から、
この形式化されたものをもうちょっと訳してください.
1、async関数を実行した後、いつもpromiseオブジェクトに戻りました.2、awaitがある行の文は同期です.
1は、外部から見て
総合1、2は、形式的に見ると、「
したがって、上記のフレームの外観だけからすれば、
今回の「ES 6を読み返す」シリーズの原則を受けていますが、細部の理解と具体的な実現過程を追求しています.私たちは引き続きこの「形式化」の理解を強化します.
async+awaitのさらなる理解
このような非同期動作
したがって,すべての検証は,完全に一致した全体が非同期関数であり,内部全体が同期の総括である.
awaitはどうやってその後の文を実行しますか?
第二に、他の
特に、
実は、非同期プログラミングについては、モジュール間の非同期プログラミング、非同期ユニットテスト、非同期エラー処理、何が良い実践ですか?All in allは、紙面に限られています.ここではまとめません.最後に、
JavaScript
プログラミングの重要な事項である.非同期方式については、ES6
は、まず状態管理に基づくPromise
が現れ、次いでGenerator + co
が現れ、次いでES7
のasync + await
案が現れた.本文力は最も簡明な方法で
async + await
を通じさせることを求める.非同期プログラミングのいくつかのシーン
まず、一般的な問題から開始します.1つの
for
サイクルでは、どのように非同期的に繰り返し印刷されますか?私たちはこの問題について、クローズドまたは
ES6
に規定されたlet
ブロック級のスコープで答えたいと思います.for (let val of [1, 2, 3, 4]) {
setTimeout(() => console.log(val),100);
}
// => :1, 2, 3, 4
ここで述べたのは、非同期キューにおいて所定の順序で並べられた均一に起こる非同期ステップである.非同期が均一に発生しない場合、非同期キューに登録されている順序は乱れている.
for (let val of [1, 2, 3, 4]) {
setTimeout(() => console.log(val), 100 * Math.random());
}
// => , :4, 2, 3, 1
戻ってきた結果は乱序で制御できないことです.これは元々最も真実な非同期です.もう一つの場合は、サイクルの中で、前の非同期を実行してほしいなら、後の非同期を実行してください.どうすればいいですか?for (let val of ['a', 'b', 'c', 'd']) {
// a ,
// b,
}
これは複数の非同期の「シリアル」ではないですか?変调
callback
に非同期の操作を入れ込んで、やり直しの方式で、この问题を解决したのではないですか?あるいは、Promise + then()
積層ネストを使っても問題は解決されます.しかし、あえてこの入れ子をループの中に書くには、まだ一週間はかかります.すみません、もっといい方法がありますか?非同期化案
考えてみてください.もしいくつかのデータをサーバに送信するなら、前のバッチが成功した(すなわちサーバが成功した応答を返した)だけが次のデータの送信を開始します.そうでなければ送信を終了します.これは典型的な「forサイクルにおける相互依存非同期動作」の例である.
このような「シリアル」の非同期は、本質的には同期として扱うことができることは明らかである.乱序の非同期と比べて、より多くの時間がかかった.道理から言えば、プログラムは非同期で実行したいです.つまり、ブロックをスキップするために、より少ない時間で費用がかかります.逆に、一連の非同期の「シリアル」が必要なら、どうやってプログラミングすればいいですか?
この「シリアル」非同期については、
ES6
があると非常に簡単にこの問題を解決します.async function task () {
for (let val of [1, 2, 3, 4]) {
// await
let result = await send(val);
if (!result) {
break;
}
}
}
task();
文面から見れば、今回の循環は結果が出たら、また次の循環を行います.そのため、サイクルは一回実行するごとに停止されます.サイクルが終わるまで一回停止されます.このコードが実現し、幾重にもネストされた「逆転地獄」の問題がよく解消され、認知度が低下しました.これは非同期問題の同期化案である.このスキームに関しては、
Promise
が主に非同期の問題を解決しているとしたら、async + await
が主に解決しているのは非同期の問題を同期化し、非同期プログラミングの認知負担を低減することである.async+await「外異内同」
最初にこのAPIに接触した時、煩雑な文書を見ていて、主に非同期化を解決するために
async + await
が使われているということが分かりました.実はそうではないです.上記の例から、
async
キーワードは、
文を表しています.この
には、行await
文があり、動作の同期実行が告示されています.そして、上下に隣接するコードは順次実行されます.この形式化されたものをもうちょっと訳してください.
1、async関数を実行した後、いつもpromiseオブジェクトに戻りました.2、awaitがある行の文は同期です.
1は、外部から見て
task
方法が実行された後にPromise
オブジェクトに戻ることを示しており、Promise
が戻ってくるので、task
は非同期的な方法であることが理解できる.間違いなくこのように使います.task().then((val) => {alert(val)})
.then((val) => {alert(val)})
2は、task
関数の内部において、非同期が「削る」ことによって同期されていることを示している.全体としては、ちょっと時間がかかる機能を実行するだけです.総合1、2は、形式的に見ると、「
task
全体は非同期関数であり、内部全体は同期している」ということで、「異内同」と略称します.
は分かりやすいです.実装では、私たちは逆方向にしてもいいです.言語レベルでasync
のキーワードを起動させると、関数実行の最後にpromise
のバックを強制的に追加します.async fn () {
let result;
// ...
// promise
return isPromise(result)?
result : Promise.resolve(undefined);
}
はどうやってできますか?実際のawait
呼び出しは、結果を取得し、
を変更させるまで、後のステートメント(関数)に再帰的に実行させ、resolve
を落とします.resolve
だけが落ちて、await
行のコードが実行されたと見なされます.したがって、外部は大きなfor
サイクルであるが、for
全体のサイクルは順次連続している.したがって、上記のフレームの外観だけからすれば、
async + await
の意味は分かりにくい.使うのも簡単ですが、逆にPromise
は身につけなければならない基礎です.今回の「ES 6を読み返す」シリーズの原則を受けていますが、細部の理解と具体的な実現過程を追求しています.私たちは引き続きこの「形式化」の理解を強化します.
async+awaitのさらなる理解
このような非同期動作
longTimeTask
があり、すでにPromise
で包装されている.この関数を用いて一連の検証を行った.const longTimeTask = function (time) {
return new Promise((resolve, reject) => {
setTimeout(()=>{
console.log(` ${time||'xx'} , `);
resolve({'msg': 'task done'});
}, time||1000)
})
}
async関数の実行状況async exec1
関数の返却結果とawait
コマンドの実行結果を参照したい場合:const exec1 = async function () {
let result = await longTimeTask();
console.log('result after long time ===>', result);
}
//
exec1();
// => xx ,
// => result after long time ===> Object {msg: "task done"}
//
console.log(exec1());
// => Promise {[[PromiseStatus]]: "pending",...}
// =>
以上の2ステップが実行され、関数体内ではexec1
が同期し、プログレッシブに実行されること、すなわち非同期動作が先に実行され、その後にconsole.log()
が印刷されることが明確に証明された.exec1()
の実行結果は、Promise
の最初のフレームPromise ...
が飛び出すので、exec1
の関数の内部実行ログです.したがって,すべての検証は,完全に一致した全体が非同期関数であり,内部全体が同期の総括である.
awaitはどうやってその後の文を実行しますか?
await
に戻って、後の文をどうやって実行するかを確認してください.longTimeTask()
の後ろに直接then()
を戻してください.2つの場合:1)then()
の中でもう何も戻りません.then()
の中で引き続き手動で他のpromise
に戻ります.const exec2 = async function () {
let result = await longTimeTask().then((res) => {
console.log('then ===>', res.msg);
res.msg = `${res.msg} then refrash message`;
// return promise
return Promise.resolve(res);
});
console.log('result after await ===>', result.msg);
}
exec2();
// => TypeError: Cannot read property 'msg' of undefined
// =>
まず、longTimeTask()
にthen()
回付が追加されました.その回調列列のqueueに置いただけです.つまり、await
コマンドの後はいつも一つの
です.ただし、上記のコードの書き方は迷惑です.(比較的良い実践提案は、longTimeTask
方法の後ろのthen()
をlongTimeTask
関数体に移してカプセル化することである)第二に、他の
promise
に手動で戻ってきても、longTimeTask()
方法の最終的なresolve
に関連する内容は異なる.言い換えれば、await
コマンドは、後のpromise
のresolve
の結果を抽出し、result
の違いを直接に引き起こす.特に、
await
コマンドは、resolve
の結果だけを認識し、reject
の結果に対してエラーを報告する.上記return
を以下のreturn
文で置き換えて検証しても良い.return Promise.reject(res);
最後に実は、非同期プログラミングについては、モジュール間の非同期プログラミング、非同期ユニットテスト、非同期エラー処理、何が良い実践ですか?All in allは、紙面に限られています.ここではまとめません.最後に、
async + await
は本当に優雅なスキームである.