async/await 基本 — async function f(){ const r = await p }
async/await は「非同期処理を同期処理のように読みやすく書く」ための構文です。Promise を裏で使いつつ、直線的な書き味で成功と失敗を表現できます。
基本の書き方
// async を付けた関数は「必ず Promise を返す」
async function greet() {
return "hello"; // 実体は Promise.resolve("hello")
}
greet().then(console.log); // "hello"
JavaScript// await は「Promise が解決されるまで待って結果を受け取る」
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function run() {
await delay(300); // 300ms待つ
const value = 42; // 待った後の処理
console.log(value); // 42
}
run();
JavaScript- async: 関数に付けると、その関数は常に Promise を返す。return 値は自動で resolve される。
- await: Promise の完了(成功/失敗)を“その場で”待つ。成功なら値、失敗なら例外として投げられる。
エラーハンドリング(try/catch/finally)
async function fetchUser(id) {
// 疑似API
return new Promise((resolve, reject) => {
setTimeout(() => {
id > 0 ? resolve({ id, name: "Aki" }) : reject(new Error("Invalid id"));
}, 200);
});
}
async function main() {
try {
const user = await fetchUser(1);
console.log("OK:", user);
} catch (e) {
console.error("NG:", e.message);
} finally {
console.log("終わり");
}
}
main();
JavaScript- ポイント: await が投げる例外を try/catch で受ける。後処理は finally に集約。
逐次実行と並列実行の使い分け
逐次(順番に待つ)
async function sequential() {
const a = await task(300); // 300ms
const b = await task(200); // 次に200ms
return [a, b]; // 合計約500ms
}
JavaScript並列(同時に走らせて、まとめて待つ)
async function parallel() {
const pa = task(300); // すぐ開始
const pb = task(200); // すぐ開始
const [a, b] = await Promise.all([pa, pb]); // 約300ms
return [a, b];
}
function task(ms) {
return new Promise(res => setTimeout(() => res(ms), ms));
}
JavaScript- 直感: 順番依存なら逐次、依存がないなら Promise.all で並列に。
よく使うテンプレート集
ラップ関数(コールバック→Promise に包む)
function delay(ms) {
return new Promise(res => setTimeout(res, ms));
}
JavaScriptタイムアウトを付ける
function withTimeout(promise, ms) {
const t = new Promise((_, rej) => setTimeout(() => rej(new Error("Timeout")), ms));
return Promise.race([promise, t]);
}
async function loadWithTimeout() {
const p = new Promise(res => setTimeout(() => res("done"), 500));
return await withTimeout(p, 300); // 300msでタイムアウト
}
JavaScriptリトライ(固定回数)
async function retry(fn, times = 3) {
let lastErr;
for (let i = 0; i < times; i++) {
try { return await fn(); }
catch (e) { lastErr = e; }
}
throw lastErr;
}
// 使用例
retry(() => fetchUser(1)).then(console.log).catch(console.error);
JavaScript順番保証つきキュー(逐次で処理)
class Queue {
#current = Promise.resolve();
enqueue(task) {
const run = async () => task(); // task は async 関数でもOK
this.#current = this.#current.then(run, run);
return this.#current;
}
}
JavaScriptよくある落とし穴と対策
- await を忘れる: Promise を返すだけで中身が実行されず、意図した順序にならない。
- 対策: その場で結果が必要なら必ず await、並列したいなら意図的に await を遅らせる。
- new Promise の乱用: すでに Promise を返す API をさらに new Promise で包むのはアンチパターン。
- 対策: 既存の Promise をそのまま await。
- try/catch の範囲が狭い/広すぎる: 失敗箇所が分かりづらくなる。
- 対策: 失敗しうる await の粒度で囲む。後処理は finally。
- 逐次にしてしまう: 依存関係がないのに連続 await で遅くなる。
- 対策: 先に Promise を作って Promise.all。
- トップレベル await の注意: モジュール(ESM)でのみ使用可。複数 await で初期化を遅らせると起動時間に影響。
- 対策: 初期化は並列化、必要最小限の待機に絞る。
練習問題(手を動かして覚える)
// 1) 基本: awaitで値を受け取る
async function ex1() {
const r = await Promise.resolve("OK");
console.log(r); // "OK"
}
ex1();
// 2) try/catch: 例外を捕まえる
async function ex2() {
try {
await Promise.reject(new Error("fail"));
} catch (e) {
console.log("caught:", e.message); // "fail"
}
}
ex2();
// 3) 逐次 vs 並列
const t = ms => new Promise(res => setTimeout(() => res(ms), ms));
async function seq() {
const a = await t(200);
const b = await t(300);
console.log("seq:", a + b); // 約500ms
}
async function par() {
const [a, b] = await Promise.all([t(200), t(300)]);
console.log("par:", a + b); // 約300ms
}
seq(); par();
// 4) タイムアウト付きで取得
async function ex4() {
const slow = new Promise(res => setTimeout(() => res("slow"), 400));
try {
const r = await withTimeout(slow, 200);
console.log(r);
} catch (e) {
console.log("timeout:", e.message);
}
}
ex4();
JavaScript直感的な指針
- 成功は値、失敗は例外で受ける。
- 依存がなければ並列化して Promise.all。
- await の位置で順序が決まる。意図して置く。
- try/catch/finally で失敗と後始末を明確に。
- 既存 Promise はそのまま await、余計なラップをしない。
