サーチ…
前書き
async
とawait
インライン非同期アクションを表現するために約束し、発電機の上に構築。これにより、非同期またはコールバックコードのメンテナンスがはるかに容易になります。
async
キーワードを持つ関数はPromise
返し、その構文で呼び出すことができます。
async function
の中では、 await
キーワードは任意のPromise
に適用することができ、約束が解決された後にawait
れた後にすべての関数本体が実行されます。
構文
- 非同期関数foo(){
...
asyncCall()を待つ
} - 非同期関数(){...}
- async()=> {...}
- (async()=> {
const data = asyncCall()を待機します。
console.log(data)})()
備考
非同期関数は、約束事と生成者の構文上の砂糖です。コードを読みやすく、保守しやすく、エラーをより簡単に捕らえることができ、インデントのレベルが低くなります。
前書き
async
として定義された関数は、非同期アクションを実行できますが、引き続き同期的に見える関数です。それが完了した方法は、 await
キーワードを使用しawait
、 プロミスが解決または拒否するのを待つ間に関数を延期することです。
注:非同期機能は、ECMAScript 2017標準に組み込む予定のステージ4(「完成」)提案です。
たとえば、約束ベースのFetch APIを使用すると、次のようになります 。
async function getJSON(url) {
try {
const response = await fetch(url);
return await response.json();
}
catch (err) {
// Rejections in the promise will get thrown here
console.error(err.message);
}
}
非同期関数は常にPromise自体を返します。したがって、他の非同期関数でも使用できます。
矢印機能スタイル
const getJSON = async url => {
const response = await fetch(url);
return await response.json();
}
インデントが少ない
約束:
function doTheThing() {
return doOneThing()
.then(doAnother)
.then(doSomeMore)
.catch(handleErrors)
}
非同期関数の場合:
async function doTheThing() {
try {
const one = await doOneThing();
const another = await doAnother(one);
return await doSomeMore(another);
} catch (err) {
handleErrors(err);
}
}
戻り値が最下位ではなく、最上位ではないことに注意し、言語のネイティブエラー処理機構( try/catch
)を使用します。
待機している演算子の優先順位
await
キーワードを使用する場合は、オペレータの優先順位を念頭に置いておく必要があります。
他の非同期関数getUnicorn()
を呼び出す非同期関数があり、 Unicorn
クラスのインスタンスに解決するPromiseを返すとします。次に、そのクラスのgetSize()
メソッドを使用してユニコーンのサイズを取得します。
次のコードを見てください:
async function myAsyncFunction() {
await getUnicorn().getSize();
}
一見すると、それは有効と思われますが、そうではありません。演算子の優先順位のために、次のものと同等です:
async function myAsyncFunction() {
await (getUnicorn().getSize());
}
ここでは、PromiseオブジェクトのgetSize()
メソッドを呼び出そうとします。これは私たちが望むものではありません。
代わりに、我々は最初にユニコーンを待つ必要があることを示すために括弧を使用し、結果のgetSize()
メソッドを呼び出す必要がありgetSize()
:
async function asyncFunction() {
(await getUnicorn()).getSize();
}
もちろん。たとえば、 getUnicorn()
関数が同期であってもgetSize()
メソッドが非同期であった場合など、以前のバージョンが有効な場合がありました。
約束事と比較した非同期機能
async
関数はPromise
型を置き換えません。それらは、約束を簡単にするための言語キーワードを追加します。彼らは交換可能です:
async function doAsyncThing() { ... }
function doPromiseThing(input) { return new Promise((r, x) => ...); }
// Call with promise syntax
doAsyncThing()
.then(a => doPromiseThing(a))
.then(b => ...)
.catch(ex => ...);
// Call with await syntax
try {
const a = await doAsyncThing();
const b = await doPromiseThing(a);
...
}
catch(ex) { ... }
promiseの連鎖を使用する関数はすべて、 await
を使用しawait
書き直すことができます。
function newUnicorn() {
return fetch('unicorn.json') // fetch unicorn.json from server
.then(responseCurrent => responseCurrent.json()) // parse the response as JSON
.then(unicorn =>
fetch('new/unicorn', { // send a request to 'new/unicorn'
method: 'post', // using the POST method
body: JSON.stringify({unicorn}) // pass the unicorn to the request body
})
)
.then(responseNew => responseNew.json())
.then(json => json.success) // return success property of response
.catch(err => console.log('Error creating unicorn:', err));
}
この関数は、次のようにasync
/ await
を使用して書き換えることができます。
async function newUnicorn() {
try {
const responseCurrent = await fetch('unicorn.json'); // fetch unicorn.json from server
const unicorn = await responseCurrent.json(); // parse the response as JSON
const responseNew = await fetch('new/unicorn', { // send a request to 'new/unicorn'
method: 'post', // using the POST method
body: JSON.stringify({unicorn}) // pass the unicorn to the request body
});
const json = await responseNew.json();
return json.success // return success property of response
} catch (err) {
console.log('Error creating unicorn:', err);
}
}
newUnicorn()
このasync
型はPromise
を返すように見えますが、実際には複数のキーワードをawait
ます。それぞれがPromise
返したので、本当に私たちは鎖ではなく約束の集まりを持っていました。
実際には、それをfunction*
ジェネレータと考えることができ、それぞれがyield new Promise
ことをawait
ています。しかし、機能を続けるためには、それぞれの約束の結果が次に必要とされます。これは、関数に追加のキーワードasync
が必要な理由(また、約束を呼び出すときにawait
キーワード)は、Javascriptにこの繰り返しのオブザーバを自動的に作成するように指示するためです。 async function newUnicorn()
によって返されたPromise
は、この繰り返しが完了すると解決されます。
実際には、それを考慮する必要はありません。約束を隠すのをawait
て、 async
はジェネレータの反復を隠します。
async
関数を約束のように呼び出すことができ、約束またはasync
関数をawait
ことができます。 .then()
使わずに約束を実行できるのと同じように、非同期関数をawait
必要はありません。
また、使用することができますasync
生命維持を使用すると、すぐにそのコードを実行する場合:
(async () => {
await makeCoffee()
console.log('coffee is ready!')
})()
非同期でループすることを待つ
async await inループを使用する場合、これらの問題のいくつかが発生する可能性があります。
forEach
内部でforEach
するだけでは、 Unexpected token
エラーが発生します。
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(e => {
const i = await somePromiseFn(e);
console.log(i);
});
})();
これは、誤って矢印の機能をブロックとして見たことが原因です。 await
はasync
ではないコールバック関数のコンテキストで行われます。
インタプリタは私たちを上記のエラーから守りますが、 forEach
コールバックにasync
を追加すると、エラーはスローされません。これで問題は解決すると思うかもしれませんが、期待どおりに動作しません。
例:
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(async(e) => {
const i = await somePromiseFn(e);
console.log(i);
});
console.log('this will print first');
})();
これは、コールバックの非同期関数は、親の非同期関数ではなく、自分自身を一時停止できるためです。
あなたは約束を返すasyncForEach関数を書くことができ、asyncForEach
await asyncForEach(async (e) => await somePromiseFn(e), data )
ようなことができます。しかし、これを行うにはより良い方法があります。それは単にループを使うことです。
for-of
ループまたはfor/while
ループを使用することができます。どちらを選択するかは重要ではありません。
(async() => {
data = [1, 2, 3, 4, 5];
for (let e of data) {
const i = await somePromiseFn(e);
console.log(i);
}
console.log('this will print last');
})();
しかし、別のキャッチがあります。このソリューションはsomePromiseFn
への各呼び出しが完了するのをsomePromiseFn
から、次の呼び出しを反復処理します。
これは、 somePromiseFn
呼び出しを実際に順番に実行したい場合には便利ですが、同時に実行する場合は、 Promise.all
をawait
必要があります。
(async() => {
data = [1, 2, 3, 4, 5];
const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
console.log(...p);
})();
Promise.all
は、唯一のパラメータとして約束の配列を受け取り、約束を返します。アレイ内のすべての約束が解決されると、返された約束も解決されます。私たちはその約束をawait
おり、解決されたら私たちの価値観はすべて利用可能です。
上記の例は完全に実行可能です。 somePromiseFn
関数は、タイムアウトのある非同期エコー関数として作成できます。 babel-replのサンプルを少なくともstage-3
プリセットで試してみて、出力を確認することができます。
function somePromiseFn(n) {
return new Promise((res, rej) => {
setTimeout(() => res(n), 250);
});
}
同時非同期(パラレル)操作
多くの場合、非同期操作を並行して実行する必要があります。 async
/ await
プロポーザルでこれをサポートする直接構文がありますが、 await
ことは約束を待つため、 Promise.all
複数の約束を一緒にPromise.all
して待機させることができます:
// Not in parallel
async function getFriendPosts(user) {
friendIds = await db.get("friends", {user}, {id: 1});
friendPosts = [];
for (let id in friendIds) {
friendPosts = friendPosts.concat( await db.get("posts", {user: id}) );
}
// etc.
}
これは、それぞれの友人の投稿をシリアルに取得するために各クエリを実行しますが、同時に実行できます。
// In parallel
async function getFriendPosts(user) {
friendIds = await.db.get("friends", {user}, {id: 1});
friendPosts = await Promise.all( friendIds.map(id =>
db.get("posts", {user: id})
);
// etc.
}
これはIDのリストをループして約束の配列を作成します。 すべての約束が完了するのをawait
でしょう。 Promise.all
はそれらを1つの約束にまとめますが、並行して実行されます。