最初に
本記事で紹介することは、JSでforEachやmap等のコールバック関数で処理をするメソッドを使い始めたときに、誰もがやらかしてしまいそうになる内容をまとめてみました。
実はこのコードは、最初に「end」と表示される
const squareNum = (num) => new Promise((resolve, reject) => resolve(num*2));
[2].forEach(async(num) => console.log(await squareNum(num)));// 最後に表示される
console.log("end");// 最初に表示される
配列内の2倍値を表示するために、forEachとsquareNum関数を使う。
しかし、非同期処理として動かしたくないため、forEach内のコールバック関数にasync/awaitを使うが、同期処理化されず最初に「end」が表示されて、最後に「4」が表示されます。
またmapやfind等のコールバック関数で処理をするメソッドも同期処理化されません。
じゃあ、どうやって非同期処理を同期処理化させればいいの?
1. 普通にfor文を使う
プライドを捨てて、普通にfor文を使いましょう!!
const squareNum = (num) => new Promise((resolve, reject) => resolve(num*2));
(async () => {
for (const num of [2]) {
console.log(await squareNum(num)); // 最初に表示される
}
console.log("end"); // 最後に表示される
})();
2. forEachにもawaitを使う
async関数内部でしか使えない制限がありますが、これも動きます。
const squareNum = (num) => new Promise((resolve, reject) => resolve(num*2));
(async () => {
await [2].forEach(async (num) => console.log(await squareNum(num))); // 最初に表示される
console.log("end"); // 最後に表示される
})();
3. ループ処理外で非同期化できないか設計を変更する
JSは表現度が高い言語です。
なので思い切って設計を変更すること一つの手です。
今回の例題であれば、ループ処理をsquareNum関数に移動させました。
そのため、console.log内部にawaitをするだけでちゃんと表示されるようになります。
const squareNum = (nums) => new Promise((resolve, reject) => resolve(nums.map((num) => num * 2)));
(async () => {
console.log(await squareNum([2]));// 最初に表示される
console.log("end"); // 最後に表示される
})()