並列 await を一言でいうと
「並列 await」は、
“複数の非同期処理を同時にスタートさせて、あとからまとめて結果を受け取る書き方” です。
普通に await を縦に並べると、A が終わってから B、B が終わってから C という「直列実行」になります。
それに対して並列 await では、
- 先に全部の Promise を「開始」しておいて
- その Promise たちを
await(またはPromise.all)で「まとめて待つ」
という形にすることで、
全体の待ち時間を短くできる 場面が出てきます。
ここが重要です。
「とりあえず全部 await」と書くのではなく、
“本当に順番でないとダメか? 同時にやってもいい処理じゃないか?” を考えたうえで、意識的に並列 await を使う のがポイントです。
まず「直列 await」との違いを体感する
直列 await の動き(1つずつ順番)
まずは普通の(直列)await から。
async function runSerial() {
console.log("直列開始");
const a = await taskA(); // A が終わるまで待つ
console.log("A 完了:", a);
const b = await taskB(); // そのあと B を実行して待つ
console.log("B 完了:", b);
console.log("直列終了");
}
JavaScriptもし taskA も taskB も 1 秒かかる処理なら、
全体でだいたい 2 秒かかります。
この書き方が悪いわけではなく、
「B は A の結果を使うから、A が終わってからでないと始められない」
というケースでは、むしろこれが正解です。
並列 await の動き(同時にスタート)
A と B が「互いに依存していない処理」なら、
先に両方「スタート」させてしまうことができます。
async function runParallel() {
console.log("並列開始");
const promiseA = taskA(); // A スタート(待たない)
const promiseB = taskB(); // B もスタート(待たない)
const a = await promiseA; // ここで A の完了を待つ
console.log("A 完了:", a);
const b = await promiseB; // ここで B の完了を待つ
console.log("B 完了:", b);
console.log("並列終了");
}
JavaScripttaskA() と taskB() を呼んだ瞬間に、
どちらもバックグラウンドで動き始めます。
もし両方とも 1 秒かかる処理なら、
全体ではだいたい「1 秒ちょっと」で終わります。
ここが重要です。
並列 await の本質は、「await の数」ではなく「Promise をいつ作るか」です。await を後ろにずらすことで、“開始は一緒、待つのはあと” という形にできます。
基本の並列 await の書き方①:Promise 変数を使う
パターンの形
一番素直な「並列 await」は、この形です。
async function runParallel() {
const promiseA = taskA(); // ここで A が動き出す
const promiseB = taskB(); // ここで B も動き出す
const a = await promiseA; // A の結果を待つ
const b = await promiseB; // B の結果を待つ
console.log(a, b);
}
JavaScriptポイントを整理すると、
taskA()/taskB()を呼んだ時点で、「非同期処理のスタート」- その戻り値(Promise)を変数
promiseA,promiseBに保存 - 必要なタイミングで
await promiseA,await promiseBする
という流れになっています。
具体例:ユーザー情報と設定を同時に取得
よくあるケースで考えてみます。
function fetchUser() {
return new Promise(resolve => {
setTimeout(() => {
resolve({ id: 1, name: "Taro" });
}, 1000);
});
}
function fetchSettings() {
return new Promise(resolve => {
setTimeout(() => {
resolve({ theme: "dark" });
}, 1000);
});
}
JavaScriptこれを並列で取ってくる関数:
async function loadUserAndSettings() {
const userPromise = fetchUser(); // ユーザー取得スタート
const settingsPromise = fetchSettings(); // 設定取得スタート
const user = await userPromise;
const settings = await settingsPromise;
console.log("ユーザー:", user);
console.log("設定:", settings);
}
JavaScriptfetchUser と fetchSettings はお互いの結果を必要としていません。
だから、同時に始めてしまって問題ないわけです。
ここが重要です。
「Promise を変数に持っておき、あとから await する」は、
並列実行の基本テクニックです。
“スタートタイミング”と“待つタイミング”を分けて考える感覚を、ここで掴んでください。
基本の並列 await の書き方②:Promise.all を使う
Promise.all + await の定番パターン
もう一つ、実務でよく使われるのが Promise.all です。
async function runParallelAll() {
const [a, b] = await Promise.all([
taskA(),
taskB(),
]);
console.log(a, b);
}
JavaScriptこの一行でやっていることは、
taskA()とtaskB()を同時にスタートさせる- どちらも resolve されるまで待つ
- 結果を配列
[aの結果, bの結果]として返す
という動きです。
Promise.all は、
「複数 Promise をまとめて待ち、結果を配列として返す」関数 と覚えておけば大丈夫です。
3つ以上でも同じ
async function run() {
const [a, b, c] = await Promise.all([
taskA(),
taskB(),
taskC(),
]);
console.log(a, b, c);
}
JavaScriptこのときも、A・B・C は同時にスタートします。
全ての Promise が成功したときだけ、
それぞれの結果が [a, b, c] に入ります。
どちらを使うべきか(Promise 変数 vs Promise.all)
どちらも「並列に実行」することに変わりはありません。
違いは主に、
- Promise 変数方式
- 各結果を「別々に」待ったり処理したりしたいときに向く
- 自分で
await promiseA; await promiseB;と書く
- Promise.all 方式
- 「全部そろってからまとめて使いたい」ときにわかりやすい
- 結果を同時に受け取りたいときに向く
というイメージです。
ここが重要です。
Promise.all は「全部成功したときだけ一気に使う」感じ。
個別の Promise 変数は「並列スタートはするけど、待つタイミングは自分でコントロール」したいときに強い。
場面に応じて、読みやすい方を選べばOKです。
並列 await を使ってはいけない場面・向いている場面
並列に「してはいけない」場面
次のような場合は、並列 await ではなく直列 await を使うべきです。
例:前の結果が次の処理に必要なとき。
async function run() {
const user = await fetchUser(); // ユーザーが必要
const posts = await fetchPosts(user.id); // user.id がないと取得できない
const comments = await fetchComments(posts[0].id); // 投稿IDが必要
}
JavaScriptここで fetchPosts(user.id) と fetchComments(posts[0].id) を同時に始めたくても、posts が無いと posts[0].id が決まらないので、
そもそも並列にしようがありません。
つまり、
- B が A の結果に依存している
- C が B の結果に依存している
ような「階段構造」のときは、直列 await が正しい です。
並列に「するべき」場面
逆に、例えば次のようなものは並列向きです。
- ユーザー情報とお知らせ情報を同時に取得
- 3つの外部 API から、それぞれ統計情報を取ってくる
- 複数ファイルを同時に読み込む
これらはお互いの結果を利用せず、
「全部揃えばいい」だけなので、
積極的に並列 await を使ったほうが パフォーマンスが良くなります。
ここが重要です。
「本当に順番にやる必要があるか?」
「連続で書いているだけで、実は関係のない処理じゃないか?」
を一度冷静に見直す癖をつけると、“並列にするチャンス” に気づけるようになります。
並列 await とエラーの扱い(Promise.all の注意点)
Promise.all は「どれか一つでも失敗したら、全体が失敗」
Promise.all には大事な性質があります。
async function run() {
try {
const [a, b] = await Promise.all([
taskA(),
taskB(),
]);
console.log(a, b);
} catch (err) {
console.error("どれか一つが失敗:", err);
}
}
JavaScriptこのとき、
taskAが失敗(reject)してもtaskBが失敗しても
どちらか一つでも失敗した瞬間に、Promise.all 全体が reject になります。
残りの処理が終わるのを待たずにエラーとして扱われるので、
「片方が失敗しても、もう片方の結果は欲しい」という場合には向きません。
片方だけ失敗しても結果が欲しい場合
例えば、「2つの API に並列で問い合わせて、
片方がダメでも、成功したほうの結果だけでも使いたい」というケースでは、
次のように個別に Promise を await しつつ try / catch する方法があります。
async function run() {
let a, b;
try {
a = await taskA();
} catch (err) {
console.error("taskA 失敗:", err);
}
try {
b = await taskB();
} catch (err) {
console.error("taskB 失敗:", err);
}
console.log("結果:", { a, b });
}
JavaScriptもちろんこの場合も、「並列にスタートさせる」ことはできます。
async function run() {
const promiseA = taskA();
const promiseB = taskB();
let a, b;
try {
a = await promiseA;
} catch (err) { /* ... */ }
try {
b = await promiseB;
} catch (err) { /* ... */ }
}
JavaScriptここが重要です。
Promise.all は「全部成功してくれないと困る」ときに使う。
「失敗しても、成功した分だけでも欲しい」なら、個別に await+try / catch のほうが柔軟に扱えます。
並列にするかどうかだけでなく、エラーの扱いもセットで設計するのがポイントです。
初心者として「並列 await の書き方」で本当に押さえてほしいこと
並列 await の基本形は 2 つだけです。
- 「Promise 変数を作ってから await」する形
const pA = taskA(); const pB = taskB(); const a = await pA; const b = await pB; - 「Promise.all と await」を組み合わせる形
const [a, b] = await Promise.all([taskA(), taskB()]);
どちらも、「処理自体は同時にスタート」しています。
違いは「結果の受け取り方」と「エラーのまとめ方」です。
そして何より大事なのは、
- 前の結果が必要な処理は、無理に並列にしない(素直に直列 await)
- お互いに独立している処理は、意識的に並列 await を検討する
Promise.allは「全部成功して当たり前」の場面で使う
という判断の軸です。
ここが重要です。
“どこに await を置くか” は、“時間の流れをどうデザインするか” という話です。
「いまは直列だけど、本当は同時に進められるかも?」という視点でコードを見直していくと、
async / await の「設計する楽しさ」が少しずつ見えてきます。
最後に、練習としてこんなことをやってみてください。
// 1. どちらも1秒後に結果を返す taskA と taskB を作る。
// 2. 「直列で await する関数」と「並列で await する関数」をそれぞれ書き、
// console.time / console.timeEnd を使って、処理時間の違いを測る。
// 3. 「どんな場面で直列が必要で、どんな場面で並列が使えるか」を
// 自分の言葉でコメントに書いてみる。
JavaScript自分で時間差を「体感」すると、
並列 await の価値と、使いどころの感覚がかなりはっきり見えてきます。
