
async/await周りで苦しんだ箇所の例と対策
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
どうも。CX事業本部Delivery部のえーたん(@eetann092)です。
今まで雰囲気でJavaScript(TypeScript)のasync/await、Promiseを使っていて最近苦しんだため、ハマったところの例と対策を備忘録として残しておきます。
await付け忘れ
やらかした例
まず、awaitを付け忘れた例です。
import { setTimeout } from "timers/promises";
const unit = 1000;
async function logSleepLog(msg: string) {
console.log(msg);
await setTimeout(1.5 * unit);
console.log(msg);
}
(async () => {
logSleepLog("hoo");
console.log("finish");
})();
hoo finish hoo
書き直した例
次に、awaitを付けた例です。
import { setTimeout } from "timers/promises";
const unit = 1000;
async function logSleepLog(msg: string) {
console.log(msg);
await setTimeout(1.5 * unit);
console.log(msg);
}
(async () => {
await logSleepLog("hoo");
console.log("finish");
})();
hoo hoo finish
対策
@typescript-eslint/eslint-pluginには、awaitの書き忘れを防ぐためのルールno-floating-promisesがあります。
今回はIIFE(Immediately Invoked Function Expression 即時実行関数式)を使っているため、IIFEでは怒られないようにignoreIIFEをtrueにします。
eslintの設定の例は以下です。
rules: {
"@typescript-eslint/no-floating-promises": [
"warn",
{
ignoreIIFE: true
}
],
awaitを付け忘れると、以下のようにエラーで怒ってくれます。
Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the
voidoperator. [@typescript-eslint/no-floating-promises]
forEachの中でasync/await
やらかした例
まず、forEachの中でasync/awaitを書いた例です。
import { setTimeout } from "timers/promises";
const unit = 1000;
async function logSleepLog(msg: string) {
console.log(msg);
await setTimeout(1.5 * unit);
console.log(msg);
}
(async () => {
const foo = [1, 2];
foo.forEach(async (f) => {
await logSleepLog(`${f}`);
});
console.log("finish");
})();
1 2 finish 1 2
書き直した例
次に、for ofを使って書き直した例です。以下の記事を参考にしました。
import { setTimeout } from "timers/promises";
const unit = 1000;
async function logSleepLog(msg: string) {
console.log(msg);
await setTimeout(1.5 * unit);
console.log(msg);
}
(async () => {
const foo = [1, 2];
for (const f of foo) {
await logSleepLog(`${f}`);
}
console.log("finish");
})();
1 1 2 2 finish
対策
@typescript-eslint/eslint-pluginには、async/awaitを使うべきではない書き方を怒ってくれるルールno-misused-promisesがあります。
eslintの設定の例は以下です。
rules: {
"@typescript-eslint/no-floating-promises": ["warn", { ignoreIIFE: true }],
"@typescript-eslint/no-misused-promises": "warn",
async/awaitを使うべきではない書き方では、以下のようにエラーで怒ってくれます。
Promise returned in function argument where a void return was expected. [@typescript-eslint/no-misused-promises]
Promise.allはすべてのエラーをcatchするわけではない
勘違いした例
Promise.allはすべてのエラーをcatchするわけではないようです。
以下、Promise.allと4つのエラーを起こす例です。
import { setTimeout } from "timers/promises";
const unit = 1000;
async function hoge(num:number, msg:string) {
const a = 'hoge';
await setTimeout(num * unit);
if (a===msg) {
console.log(`正常 ${num}:${msg}`)
} else {
console.log(`エラーが起きた ${num}:${msg}`)
throw `throwのメッセージ ${num}:${msg}`
}
}
(async () => {
const promises = [];
promises.push(hoge(1, 'hoge')); // 1秒後に 正常
promises.push(hoge(2, 'foo')); // 2秒後に エラー
promises.push(hoge(5, 'bar')); // 5秒後に エラー
promises.push(hoge(4, 'bar')); // 4秒後に エラー
promises.push(hoge(3, 'bar')); // 3秒後に エラー
await Promise.all(promises).catch(e => console.log(e))
console.log("finish");
})();
以下が出力です。catchされたエラーは最初の2のみです。また、Promise.allでは、catchが終わった時点で次の行("finish"の表示)の処理に移るようです。
正常 1:hoge エラーが起きた 2:foo throwのメッセージ 2:foo finish エラーが起きた 3:bar エラーが起きた 4:bar エラーが起きた 5:bar
書き直した例
Promise.allSettledを使えば、すべてのプロミスが終わってから結果を返してくれます。
ちなみに "settled" は「確立した、定まった」などの意味を持つ単語のようです(参考:英語「settled」の意味・読み方・表現 | Weblio英和辞書)。
以下が、Promise.allSettledを使って書き直した例です。
成功した場合、受け取った結果のstatusは"fulfilled"になり、valueで値を取得できます。
失敗した場合、受け取った結果のstatusは"rejected"になり、reasonで理由を取得できます。
import { setTimeout } from "timers/promises";
const unit = 1000;
async function hoge(num: number, msg: string) {
const a = "hoge";
await setTimeout(num * unit);
if (a === msg) {
console.log(`正常 ${num}:${msg}`);
} else {
console.log(`エラーが起きた ${num}:${msg}`);
throw `throwのメッセージ ${num}:${msg}`;
}
}
(async () => {
const promises = [];
promises.push(hoge(1, "hoge")); // 1秒後に 正常
promises.push(hoge(2, "foo")); // 2秒後に エラー
promises.push(hoge(5, "bar")); // 5秒後に エラー
promises.push(hoge(4, "bar")); // 4秒後に エラー
promises.push(hoge(3, "bar")); // 3秒後に エラー
await Promise.allSettled(promises).then((results) =>
results.map((result) => {
if (result.status === "rejected") {
console.log(result.reason);
}
})
);
console.log("finish");
})();
以下が出力です。すべての実行が終わってからエラーの内容を吐き出しています。
正常 1:hoge エラーが起きた 2:foo エラーが起きた 3:bar エラーが起きた 4:bar エラーが起きた 5:bar throwのメッセージ 2:foo throwのメッセージ 5:bar throwのメッセージ 4:bar throwのメッセージ 3:bar finish
参考:
リンク集
サンプルコードのGitHubのリンクは以下です。






