// 注意: 最初のバージョン、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はどのノードでも使える式の方が嬉しい。結合順がややこしいが。
間違ってたら教えてほしい。