はじめに
諸事情につき、Array.map
の中でasync/awaitを利用したのですが、思った以上に詰まった・・。
ということでメモがてら残しておきましょう。
参照(というかほとんど切り抜き元):
やりたかったこと
Arrayに入っている値を元に非同期関数を呼び出しつつ、その結果の値を整形した配列を用意する。
(最終的にjoinするだけで画面に表示したい)
dataArray:['1', '2', '3']
↓
result:['・りんご', '・みかん', '・いちご']
↓
画面
・りんご
・みかん
・いちご
結論
const dataArray = ['1', '2', '3'];
const result = await Promise.all(dataArray.map(async (data) =>
'・' + await sampleFunction(data)
));
説明
dataArray.map(async (data) => await sampleFunction(data))
ここはまあ、見たまんまの動きです。
非同期関数を同期的に呼びたいので、無名関数の頭にasync
を付け、実際に呼び出す関数にはawait
を付けています。
こうすることで、dataArray.mapは無事同期的に結果を取得できる・・と思いきや、dataArray.mapにはawaitがついていないので、この無名関数の処理終了を待ちません。
コードを分解すると、こんな感じ。
// この関数内ではきちんと同期処理化している
async mapFunction(data) {
return '・' + await sampleFunction(data);
}
let result = [];
for (const data of dataArray) {
result.push(mapFunction(data)); // こっち側が同期処理化していない
}
// そのため、Promiseの配列になってしまう
console.log(result); // [object Promise, object Promise, object Promise]
じゃあawait dataArray.map
としたら良いのでは?と思ったのですが・・。
await
はPromiseが返される関数にのみ適用されるため、「Promiseの配列」だと無力です。
※エラーにもならなかった。
await Promise.all
ということで、Promise.all
を使います。
こうすることで、配列内のPromiseが全て履行されてから次へ進むようになります。
解決!!
ちなみに・・
今回のパターンの場合、こっちでもできる。
const dataArray = ['1', '2', '3'];
const promiseResult = await Promise.all(dataArray.map(data =>
sampleFunction(data)
));
const result = promiseResult.map(elem =>
'・' + elem
);
おそらく処理速度はほぼ一緒になる・・と思う・・・。
mapを2回呼び出している分だけロスがある??と思う???
(逆にasync/await
の数が少ない分早かったりする??多分変わらないよね・・?)
さらにちなみに・・
await Promise.all(...).join('\n');
の書き方は許されませんでした。
多分Promise.all(...).join('\n')
を評価した後にawait
をする流れになるから。
下記のどちらかにしましょう。
// パターン1
(await Promise.all(...)).join('\n');
// パターン2
const promiseResult = await Promise.all(...);
const result = promiseResult.join('\n');
おわりに
JavaScriptは奥が深いけど、なんとなく好きです。
自由度が高い・・気がする・・・。