サーチ…


前書き

asyncawaitインライン非同期アクションを表現するために約束し、発電機の上に構築。これにより、非同期またはコールバックコードのメンテナンスがはるかに容易になります。

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);
 });
})();

これは、誤って矢印の機能をブロックとして見たことが原因です。 awaitasyncではないコールバック関数のコンテキストで行われます。
インタプリタは私たちを上記のエラーから守りますが、 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.allawait必要があります。

(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つの約束にまとめますが、並行して実行されます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow