Javascript->async、awaitの探索
8748 ワード
asyncを話す前にpromiseを簡単に話してください.まず、多くの人の一般的な誤った観点を修正します-->'promiseは非同期です'、コードを見てください.
印刷結果から、promiseが同期していると断定できます.では、promiseは同期していると言っています.thenは非同期です.いいえ、簡単に原因を言います:まず結果を言います:promiseのthenも同期しています.このように出力されるのは、new promise(fn)fnのr()関数が非同期でスレッドが保留され、thenに実行されると、thenのコードブロックがすぐに実行を開始し(注意して実行を開始する)、成功したコールバック関数をresovledCallbacksに入れただけで、状態修正がfulfiledに完了した場合でも、上の実行then(fn)のfn内のコード実行は非同期で動作します.すぐにconsoleを実行するわけではありません.thenの内部実装方式はpromisa仕様に従ってsettimeoutが遅延器の内部でaaaを実行するので、thenメソッドは同期関数を肯定しますが、実際には永遠に非同期です.2つのsettimeoutは非同期で成功または失敗したコールバック関数を実行することを保証しているからです.具体的にはr()内部に遅延実行コールバックが設定されており、遅延settimeoutの最小値、すなわちrこそ非同期である.
分かったでしょう?解決された問題は主に以下のようにまとめることができます.コールバック地獄、コードのメンテナンスが困難で、しばしば最初の関数の出力が2番目の関数の入力であるという現象 promiseは、複数の同時要求をサポートし、同時要求のデータ を取得することができる. promiseは非同期の問題を解決することができて、自身はpromiseが非同期の だとは言えません
promiseはもうあまり紹介しません.時間があれば、もっと深く研究してもいいです.promiseは本当にcallback hellを完全に解決しましたか.まず1つのシーンには4つの関数があり、順番に実行する必要があります.つまり、前のpromise fullが実行されるまで待たなければなりません.後のpromiseは、前のpromiseで返される値です.たとえば、
私たちは普通このように書きます.
あるいはもう少し簡素化します
美しく見えますが、第1の方法で書かず、第2の方法で書くと、可読性が悪いことがわかります.f 1()だけを見てみましょう.then(f2).then(f3).then(f4);このコードは実はf 1,f 2,f 3,f 4が何のつながりがあるのか全く見えず、f 2,f 3,f 4も入力として上位層の出力を使っているので、最も理想的な表現だと思います.
しかし、もしそうなら、私たちの関数が順次実行されていることは保証できません.入出力はおろか.こうして、私たちのasyncが登場して、あなたはこのように書くことができます.
ES 7が提案したasync関数は、JavaScriptが非同期操作に対して究極の解決策をもたらした.No more callback hell.async関数はGenerator関数のシンタックス糖である.キーワードasyncを使用して表し、関数内部でawaitを使用して非同期を表します.Generatorに比べて、Async関数の改善は次の4つの点にあります(この4つのセグメントは私が他の場所で見つけたもので、まとめもいいです):にはアクチュエータが内蔵されています.Generator関数の実行はアクチュエータに依存しなければならないが、Aysnc関数はアクチュエータを持参し、呼び出し方式は通常の関数の呼び出しと同じ である.より良い意味.asyncとawaitは*とyieldよりも意味化されている より広い適用性.Coモジュールはyieldコマンドの後ろにThunk関数またはPromiseオブジェクトしかないことを約束している.一方、async関数のawaitコマンドの後には、Promiseまたは元のタイプの値(Number,string,boolean) があります.の戻り値はPromiseです.async関数の戻り値はPromiseオブジェクトであり、Generator関数が返すIteratorオブジェクトよりも便利であり、then()メソッドを直接使用して呼び出すことができる(coモジュールは実際にはGeneratorとPromiseを結合し、自動的にGeneratorを実行する) .
asynch関数にも戻り値があり、promiseオブジェクトなので使用できます.then
ただし、関数の実行中にawaitに遭遇すると先に戻ってくるので注意してください.
私のコードのreutnr 1ですが、結果はpending状態のPromiseオブジェクトを返します.async関数内部のreturn文が返す値はthenメソッドコールバック関数のパラメータになります.
注意すべきは、私はずっと関数の実行を強調しています.awaitは実行を待つという意味ですが、外部に副作用を与えることはありません.
印刷結果から、プログラム実行中にawaitに遭遇したが、外部のコード実行をブロックしていないことを見たので、Javascriptの非同期の本質は変わっていないが、少なくともasync関数で私たちの流れをよくコントロールすることができ、この文法糖の強さを見てみましょう.ターゲット:ajaxリクエストを30回送信し、リクエストがシリアルであることを要求する(すなわち、リクエストを送信する際に前のリクエストresを待たなければならない)この問題を従来のpromisepromise.thenの方法は実装が困難であるため,まずajaxリクエストをシミュレートし,各ajaxのtimeresponseが400 msであると仮定した.
Promise実装:
Generator実装
async実装
比較してみると、さっきasyncが実はGeneratorの文法糖だと言ったのは明らかだ.asyncがどのように実現する原理なのか、理解したいのか、ジェネレータ(generator)を勉強しなければならない人もいるに違いない.結局asyncはgeneratorの文法糖にすぎないので、それをスキップして直接asyncを勉強するのはもちろん多くのことを見逃します.asyncはGenerator+オートアクチュエータに等しい.話は前の例に戻る
asyncでawaitに遭遇すると後のPromiseの戻り結果(同期を除く)を待つと言っていたので、上記のコードの実行順序はf 1->f 2->f 3なので、f 1,2,3を同時に実行させるにはどうすればいいのでしょうか.Promiseが同期していることを知っています.new Promise(...)というときには、事実上すでに実行が始まっていますが、結果がバンド状態のPに戻るだけなので、f 1,2,3を並行させたいなら仕方ありません
これはnew Promiseのコードブロックが同時に行われていることに相当し,状態がpendingからfullに変わる時間の長さはビジネスニーズや場合によって決定され,もう一つの方法はより直感的である可能性がある.
ここでreは1つの配列であり、値はそれぞれp 1,2,3に対応する.raceに変えてもいいよrace([p1, p2, p3])
リクエストが多ければmap,foreachを用いて並列に実行することもできます
mapとforEachは並行してpromise配列を実行するが,for-in for-of forはシリアルである.この2つの点を知って、私たちは多くの非同期要求を効率的に処理することができます.最後に簡単に言えばasyncのエラー処理方法promiseでの異常やrejectはtry catchではキャプチャできないことを知っています.例えば
このエラーtry catchは取得できません.function fn(){
ここでReferenceError異常を直接投げ出してasyncに入れます
不思議なことに、異常が捕獲されたが、実はこの場所は私もその本当の原因を確定していない.重点はawaitが何をしたのか、実行環境にどのような影響を与えたのか、まず私の観点を述べる.promiseは非ブロックである.つまりpromise外部のtry、catchにとって、内部のpromiseは非同期実行である.try cathchは非同期エラーをキャプチャできませんが、awaitはpromiseの実行を待つことを示し、この約束の実行結果を待つことを示し、現在のasync実行環境のコード実行を一時停止します.つまり、asyncの下で、awaitは同期的で、ブロックされていると考えることができます.このエラーは同期放出(await new Promise(...)であると考えられます投げ出したので捕獲されます.しかし、asyncの異常をキャプチャする方法はお勧めしません.1つはコード構造が混乱しているように見えます.2つはtry/catchのcatch部分に異常があれば、どうすればいいですか.だからasync()を使うことをお勧めします.catchは、asyncが戻り値の有無にかかわらずpromiseオブジェクトを返すため処理します.
asyncはreturnを使用してpromiseを返すこともできます
asyncについて簡単に紹介します.次の文章では、なぜ私たちがGeneratorを使うのが少ないのか、あるいはなぜasyncがGeneratorの文法糖なのかを説明します.
console.log(1);
let p1 = new Promise(r => { console.log(2); r() });
p1.then(_ => console.log(3));
console.log(4);
let p2 = new Promise(r => { console.log(5); r() });
p2.then(_ => console.log(6));
console.log(7);
// 1 2 4 5 7 3 6
印刷結果から、promiseが同期していると断定できます.では、promiseは同期していると言っています.thenは非同期です.いいえ、簡単に原因を言います:まず結果を言います:promiseのthenも同期しています.このように出力されるのは、new promise(fn)fnのr()関数が非同期でスレッドが保留され、thenに実行されると、thenのコードブロックがすぐに実行を開始し(注意して実行を開始する)、成功したコールバック関数をresovledCallbacksに入れただけで、状態修正がfulfiledに完了した場合でも、上の実行then(fn)のfn内のコード実行は非同期で動作します.すぐにconsoleを実行するわけではありません.thenの内部実装方式はpromisa仕様に従ってsettimeoutが遅延器の内部でaaaを実行するので、thenメソッドは同期関数を肯定しますが、実際には永遠に非同期です.2つのsettimeoutは非同期で成功または失敗したコールバック関数を実行することを保証しているからです.具体的にはr()内部に遅延実行コールバックが設定されており、遅延settimeoutの最小値、すなわちrこそ非同期である.
console.log(1);
let p1 = new Promise(r => { console.log(2); r() });
p1.then(console.log(3));
console.log(4);
let p2 = new Promise(r => { console.log(5); r() });
p2.then(console.log(6));
console.log(7);
// 1 2 3 4 5 6 7
分かったでしょう?解決された問題は主に以下のようにまとめることができます.
promiseはもうあまり紹介しません.時間があれば、もっと深く研究してもいいです.promiseは本当にcallback hellを完全に解決しましたか.まず1つのシーンには4つの関数があり、順番に実行する必要があります.つまり、前のpromise fullが実行されるまで待たなければなりません.後のpromiseは、前のpromiseで返される値です.たとえば、
function f1() {
return new Promise(resolve => {
setTimeout(_ => resolve('f1'), 500)
})
}
function f2(params) {
return new Promise(resolve => {
console.log(params);
setTimeout(_ => resolve(params + 'f2'), 500)
})
}
function f3(params) {
return new Promise(resolve => {
console.log(params);
setTimeout(_ => resolve(params + 'f3'), 500)
})
}
function f4(params) {
return new Promise(resolve => {
console.log(params);
setTimeout(_ => resolve(params + 'f4'), 500)
})
}
私たちは普通このように書きます.
f1().then(res => {
return f2(res)
}).then(res => {
return f3(res)
}).then(res => {
return f4(res)
});
あるいはもう少し簡素化します
f1().then(f2).then(f3).then(f4);
美しく見えますが、第1の方法で書かず、第2の方法で書くと、可読性が悪いことがわかります.f 1()だけを見てみましょう.then(f2).then(f3).then(f4);このコードは実はf 1,f 2,f 3,f 4が何のつながりがあるのか全く見えず、f 2,f 3,f 4も入力として上位層の出力を使っているので、最も理想的な表現だと思います.
f1();
f2();
f3();
f4();
しかし、もしそうなら、私たちの関数が順次実行されていることは保証できません.入出力はおろか.こうして、私たちのasyncが登場して、あなたはこのように書くことができます.
void (async function() {
let r1 = await f1()
let r2 = await f2(r1)
let r3 = await f3(r2)
await f4(r3)
})();
ES 7が提案したasync関数は、JavaScriptが非同期操作に対して究極の解決策をもたらした.No more callback hell.async関数はGenerator関数のシンタックス糖である.キーワードasyncを使用して表し、関数内部でawaitを使用して非同期を表します.Generatorに比べて、Async関数の改善は次の4つの点にあります(この4つのセグメントは私が他の場所で見つけたもので、まとめもいいです):
asynch関数にも戻り値があり、promiseオブジェクトなので使用できます.then
async function f() {
return 1
}
console.log(f()) // Promise { 1 }
ただし、関数の実行中にawaitに遭遇すると先に戻ってくるので注意してください.
async function f() {
await 2;
return 1
}
console.log(f()); // Promise { }
私のコードのreutnr 1ですが、結果はpending状態のPromiseオブジェクトを返します.async関数内部のreturn文が返す値はthenメソッドコールバック関数のパラメータになります.
async function f() {
await 2;
return 1
}
f().then(res => {
console.log(res) // 1
})
注意すべきは、私はずっと関数の実行を強調しています.awaitは実行を待つという意味ですが、外部に副作用を与えることはありません.
async function f() {
await 2;
console.log('a')
return 1
}
f()
console.log('b') // b a
印刷結果から、プログラム実行中にawaitに遭遇したが、外部のコード実行をブロックしていないことを見たので、Javascriptの非同期の本質は変わっていないが、少なくともasync関数で私たちの流れをよくコントロールすることができ、この文法糖の強さを見てみましょう.ターゲット:ajaxリクエストを30回送信し、リクエストがシリアルであることを要求する(すなわち、リクエストを送信する際に前のリクエストresを待たなければならない)この問題を従来のpromisepromise.thenの方法は実装が困難であるため,まずajaxリクエストをシミュレートし,各ajaxのtimeresponseが400 msであると仮定した.
function ajax(n) {
return new Promise((rs, rj) => {
setTimeout(() => {
console.log(n);
rs()
}, 400)
})
}
Promise実装:
let n = 50
let task = ajax(n);
function run() {
task.then(_ => {
--n && (task = ajax(n)) && run()
})
}
run();
Generator実装
let num = 50;
function* Ge() {
while (true) {
yield ajax(num)
}
}
let re = Ge()
function run() {
let result = re.next().value
result.then(_ => {
num-- && run()
})
}
run()
async実装
let n = 50
async function run() {
while (n--) await ajax(n)
}
run()
比較してみると、さっきasyncが実はGeneratorの文法糖だと言ったのは明らかだ.asyncがどのように実現する原理なのか、理解したいのか、ジェネレータ(generator)を勉強しなければならない人もいるに違いない.結局asyncはgeneratorの文法糖にすぎないので、それをスキップして直接asyncを勉強するのはもちろん多くのことを見逃します.asyncはGenerator+オートアクチュエータに等しい.話は前の例に戻る
void (async function() {
let r1 = await f1()
let r2 = await f2(r1)
let r3 = await f3(r2)
await f4(r3)
})();
asyncでawaitに遭遇すると後のPromiseの戻り結果(同期を除く)を待つと言っていたので、上記のコードの実行順序はf 1->f 2->f 3なので、f 1,2,3を同時に実行させるにはどうすればいいのでしょうか.Promiseが同期していることを知っています.new Promise(...)というときには、事実上すでに実行が始まっていますが、結果がバンド状態のPに戻るだけなので、f 1,2,3を並行させたいなら仕方ありません
void (async function() {
let r1 = new Promise(...)
let r2 = new Promise(...)
let r3 = new Promise(...)
await r1
await r2
await r3
})();
これはnew Promiseのコードブロックが同時に行われていることに相当し,状態がpendingからfullに変わる時間の長さはビジネスニーズや場合によって決定され,もう一つの方法はより直感的である可能性がある.
void (async function() {
let re = await Promise.all([p1, p2, p3])
})();
ここでreは1つの配列であり、値はそれぞれp 1,2,3に対応する.raceに変えてもいいよrace([p1, p2, p3])
リクエストが多ければmap,foreachを用いて並列に実行することもできます
function plist(n) {
return new Promise(resolve => {
console.log('start:' + n)
setTimeout(_ => {
resolve(n)
}, 2000)
})
}
let c = [...new Array(100).keys()]
let pros = c.map(async n => {
return await plist(n)
})
for (let p of pros) {
p.then(res => console.log('end:' + res))
}
mapとforEachは並行してpromise配列を実行するが,for-in for-of forはシリアルである.この2つの点を知って、私たちは多くの非同期要求を効率的に処理することができます.最後に簡単に言えばasyncのエラー処理方法promiseでの異常やrejectはtry catchではキャプチャできないことを知っています.例えば
try {
Promise.reject('an normal error')
} catch (e) {
console.log('error comming')
console.log(e)
}
このエラーtry catchは取得できません.function fn(){
try {
new Promise(resolve => {
JSON.parse(a)
resolve()
})
} catch (e) {
console.log('error comming')
console.log(e)
}
}
fn()
ここでReferenceError異常を直接投げ出してasyncに入れます
async function fn() {
try {
await new Promise(resolve => {
JSON.parse(a)
resolve()
})
} catch (e) {
console.log('error comming')
console.log(e)
}
}
fn()
不思議なことに、異常が捕獲されたが、実はこの場所は私もその本当の原因を確定していない.重点はawaitが何をしたのか、実行環境にどのような影響を与えたのか、まず私の観点を述べる.promiseは非ブロックである.つまりpromise外部のtry、catchにとって、内部のpromiseは非同期実行である.try cathchは非同期エラーをキャプチャできませんが、awaitはpromiseの実行を待つことを示し、この約束の実行結果を待つことを示し、現在のasync実行環境のコード実行を一時停止します.つまり、asyncの下で、awaitは同期的で、ブロックされていると考えることができます.このエラーは同期放出(await new Promise(...)であると考えられます投げ出したので捕獲されます.しかし、asyncの異常をキャプチャする方法はお勧めしません.1つはコード構造が混乱しているように見えます.2つはtry/catchのcatch部分に異常があれば、どうすればいいですか.だからasync()を使うことをお勧めします.catchは、asyncが戻り値の有無にかかわらずpromiseオブジェクトを返すため処理します.
async function fn() {
}
console.log(fn().then) // [Function ...]
asyncはreturnを使用してpromiseを返すこともできます
async function fn() {
// return await Promise.resolve(1)
// return Promise.resolve(1)
}
asyncについて簡単に紹介します.次の文章では、なぜ私たちがGeneratorを使うのが少ないのか、あるいはなぜasyncがGeneratorの文法糖なのかを説明します.