非同期 async-await

async-awaitは、「いつ完了するか分からない処理」だけど「順番に実行したい」な事をするために使います。「コールバック地獄」と呼ばれていたような読みづらいコードをシンプルに書けます。

構文

functionの前、アロー関数なら()の前にasyncと書きます。

async function foo() {
  /* ... */
}
const bar = async () => {
  /* ... */
};

そして、中でPromiseを返す関数の実行前にawaitと書けば、処理が終わるまでその場で待ってくれます。これはPromise#thenを使う方法と比べて構文を反対に書いたような形になります。つまり「関数→then→変数」から「変数→await→関数」といった具合です。

// Promise#then
asyncFn1().then(data => {/*...*/});
asyncFn2().then(() => {/*...*/});

// await
const data = await asyncFn1();
await asyncFn2();

以下は1毎にコンソールにテキストを出力する例です。

/**
 * 1秒待つ
 * (
 *    `async`と書くと`Promise`だなとわかるのでオススメしたいです
 *    TypeScript 除く
 *  )
 */
const delay = async () => new Promise(r => setTimeout(r, 1000));

(async () => {
  console.log('すぐ表示');
  await delay();
  console.log('1秒後表示');
  await delay();
  console.log('さらに1秒後');
})();

エラー処理

await文の例外はtry-catchでキャッチすることができます。(エラーでやっているけど単に勉強の為でPromise#rejectでも良い)

class OversleepError extends Error {
  constructor() {
    super('寝坊した');
    Error.captureStackTrace(this, OversleepError);
  }
}

/**
 * 寝る
 */
const sleep = async () => {
  throw new OversleepError();
};

(async () => {
  try {
    await sleep();
  } catch (err) {
    console.log(err instanceof OversleepError); // true
  }
})();

扱うのはPromiseなのでcatchで一旦受け取ってできるだけ処理することももちろんできます。

/* 上の関数定義そのまま */

const eatBreakfast = async () => {
  /* 食べる */
};

(async () => {
  const task = {
    breakfast: true
  };

  await sleep().catch(err => {
    if (err instanceof OversleepError) {
      task.breakfast = false;
      return;
    }
  });

  // 遅刻してない場合、
  // 朝食を食べる
  if (task.breakfast) {
    await eatBreakfast();
  }
})();