map()の中でasyncを使うと、中身がPromiseのまま返ってくる件
配列の各要素に対して、1件ずつ非同期処理(APIを呼ぶ、DBから取得するなど)をしたいとき、mapの中にasyncを書きたくなります。動きそうに見えて、実は思った通りの結果になりません。
Before: map の中に async を書く
async function fetchDouble(x) {
// 実際には fetch や DB アクセスをイメージしてください
return x * 2;
}
const numbers = [1, 2, 3];
const results = numbers.map(fetchDouble);
console.log(results);
// [ Promise { 2 }, Promise { 4 }, Promise { 6 } ]
resultsに入っているのは2, 4, 6ではなく、Promiseというオブジェクトが3つ並んだ配列です。Promiseオブジェクトは常にtruthy(真として扱われる値)なので、if (results[0]) { ... }のような存在チェックだけをして中身を確認したつもりになっていると、実際には値ではなく入れ物が入っているだけなのに気づかず先に進んでしまいます。
なぜこうなるのか
asyncが付いた関数は、呼び出した瞬間に処理が終わっているとは限らず、常にPromise(これから値が届くことを表す入れ物)を返します。map自体は「各要素にコールバック関数を適用して、その戻り値を集めた新しい配列を作る」だけの処理で、awaitのような「中身が届くまで待つ」機能は持っていません。そのため、mapの中でasync関数を呼んだ結果、mapは「届いていないPromiseの入れ物」をそのまま集めて返してしまいます。
After: Promise.all で全部の完了を待つ
async function main() {
const numbers = [1, 2, 3];
const promises = numbers.map(fetchDouble); // Promiseの配列
const results = await Promise.all(promises); // 全部完了するまで待つ
console.log(results);
// [ 2, 4, 6 ]
}
Promise.all()は「複数のPromiseをまとめて渡すと、全部が完了するのを待ってから、中身の値だけを集めた配列を返す」関数です。mapで作ったPromiseの配列をそのままPromise.all()に渡し、その結果をawaitすることで、ようやく2, 4, 6という実際の値が手に入ります。
見分け方: forEach との混同にも注意
似た事故として、forEachの中でawaitを使うケースもあります。
numbers.forEach(async (x) => {
const result = await fetchDouble(x);
console.log(result);
});
console.log("完了!"); // ← forEach の中身より先に実行される
forEachは各コールバックの戻り値を一切見ておらず、Promiseが返ってきても待たずに次の要素へ進みます。そのため、forEachのブロック全体が終わる前に「完了!」が先に表示されてしまいます。原因はmapの話と同じ「戻り値がPromiseのままなのに、それを見ずに先へ進んでしまう」という仕組みですが、今回は値を後で使えないことが問題なのに対し、forEach+try-catchの組み合わせではエラーそのものを取りこぼす別の事故につながります(この事故については以前Qiitaで詳しく書いたので、ここでは仕組みの共通点だけ触れておきます)。1件ずつ順番に処理したい場合はfor...ofとawaitの組み合わせ、全部並行に処理してよい場合は今回のPromise.all()が使えます。
まとめ
mapの中でasync関数を呼ぶと、戻ってくるのは値そのものではなくPromiseの配列Promiseは常にtruthyなので、そのまま条件分岐に使うと意図しない挙動になる- 全部の完了を待ってから中身を使いたいときは
await Promise.all(配列.map(async 関数))の形にする forEachは戻り値を無視するので、非同期処理を1件ずつ待ちたい場面には向かない(for...ofを使う)
AIが生成したコードにmap(async ...)だけが単独で出てきたら、その結果をそのまま使わずにPromise.allで受けているか、後続でawaitしているかを確認するようにしています。
面談なしで、今日から始められます
この講座は 未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで を全24セッションで体系化した教材付きプランです。無料相談を挟まず、申し込んだその日から教材で学習を始められます。AIが学習パートナーになって何度でも質問でき、つまずいた所だけチャットで直接サポートします。
- 今日から始める(教材完全版+月5,500円・チャット質問し放題・いつでも解約OK)→ https://menta.work/plan/20251?ref=menta-knowledge
- いきなりは不安な方へ:無料の教材体験版もあります(最初の数セッション分)→ プラン詳細をご覧ください

