ハマった
元のコード
// async function の中
try {
load().then(data => {
console.log(data)
}).catch(e => {
// ...
})
} catch (e) {
// ... 例外処理
}
わかりやすく簡単にしている。実際にはもっと複雑なコードだった。Promise にすれば
try と catch を一本化して綺麗にできるやん!と思っていた。最初は。
書き換えた
// async function の中
try {
const data = await load()
console.log(data)
} catch (e) {
// ... 例外処理
}
catch が一個減ってリファクタできたーと思っていた。確かに異なる例外処理のブロックが減ってしまっていたが、どうせ何かしらのデッドコードだろと思って消してしまった。
注: 意味的に変わってしまっているが、実際にはすごく複雑なコードで、大きな方のtryは別の例外をキャッチしていると思っていた…
何が起こったか
catch に来てた部分が、Promise の
UnhandledRejectionError になってしまっていた(のにしばらく気づかなかった)。
ここで、状況を述べておくと、 load()
はライブラリが提供してた関数で、Promiseを返すことは知っていたが、実装には詳しくなかった。これがハマる理由になった。
挙動をちゃんと追うと、ここの load()
は 同期的な例外 または Promise を返す という実装だった。期待していたのは Promise の resolve or reject だったので、ミスマッチがあったというわけ。
どう直したか
// async function の中
try {
const loading = load()
try {
const data = await loading
console.log(data)
} catch (e) {
// 非同期例外
}
} catch (e) {
// 同期例外
}
えーー!!ってコードになってしまった。でもこれが元々期待されてた挙動的に正しい…。
反省
- async/await の中でも同期例外と非同期例外を区別する
- 自分がPromiseの関数を実装するときは Promiseのインターフェースに押し込めるようしたいと思った
- 例外パターンに対するテストは常に書こう
以上、これで2日分ほどハマった現場よりの報告でした。