JavaScript
es6
babel

ES async/awaitを全力で使ってみて発見したイディオム

More than 1 year has passed since last update.

// 注意: 最初のバージョン、async function がundefinedを返すと思い込んでて、色々間違えてた

手元の趣味コード(諸事情により未公開)に向けて全力で適用してみた結果学びがあった。以下babel。

事前に確認

  • async/await は Promise と Generator の糖衣構文である
  • await は Promiseのインスタンスの式を与えると(見た目上)停止する
  • await するには async functionで囲う必要がある
  • async function は必ず非同期で実行され undefinedPromise を返す

以下イディオム

とりあえず実行したい

(async () => {
  await new Promise(done => {
    setTimeout(1000, done);
  })
})();

解説: async ブロック作ってからの即時実行。asyncブロックなしにawaitは呼べない。PubSubのSub側でreturn する必要がないときに何度か使った。

Promiseを返す関数の中で使う

fetchParamsは fetchParams() => Promise<Params> な関数だと思ってほしい。以下使いまわす。

// without async/await
function fetchXXX(){
  return fetchParams()
};

// with async/await
function fetchXXX(){
  return (async () => {
    const params = await fetchParams();
    return params;
  })();
};

解説: Promiseのラッパー関数がasyncブロックをとる。このままだと元のコードのほうが短くて見通しはいいんだけど、複数回 await する場合はthenで繋がない分、後者のほうが見通しがよくなる。

Promiseの直列化

// parallel without async/await
const p = Promise.all(items.map(item => fetchParamsByItem(item)));

// serial with async/await
const p = (async () => {
  const results = [];
  for (let item of items) {
      const params = await fetchParamsByItem(item);
      results.push(item);
  }
  return results;
})();

解説: 元の例がかなりショートコーディング気味だが、要は複数のPromiseをPromise.allで並列にさばいているという例。async/await版は直列なので遅くなっている。
注意すべきは、ここでただのmapを使うとコールバック関数によって async function から外れるので、for of で回している。 items.map(async (i) => {} みたいなコードも試したがasyncの中でasyncはbabelが例外を吐いてた。仕様の側に問題があるのかbabelに問題があるかはよくわかってないので誰か教えてくれ。

Promise関数の同期部分を待ってからawaitしたい

(async () =>{
  var p = fetchParams();
  app.registerPromiseWithPopup(p);
  await p;
})();

解説: app.registerPromiseWithPopup(p) は名前の通りpromiseを受け取って終了までポップアップでカバーをかける関数なのだが、最初のfetchParams() を await してしまうとpromiseのインスタンスが取得できない。一旦await するタイミングをずらす。

Mochaのテストケース

it('pass', async () => {
  const params = await fetchParams();
  assert.equal(params, {});
});

解説: テストケースは基本的に返り値要求されないので最初からasync関数として扱う
(実はmochaは最後にpromiseを受け取って終了を待てるのだが、非同期のハンドルという目的では同一なので使わなくて良い)

以上

async function は評価した時にfunction抜けた時にresolveされるPromiseを返してほしいなと思った。 Promise返してた。

あと見通し悪くなる可能性あるけど、awaitはどのノードでも使える式の方が嬉しい。結合順がややこしいが。
間違ってたら教えてほしい。