Edited at

async await に書き換えて、Promiseと 同期による例外の区別でハマった

More than 1 year has passed since last update.

ハマった


元のコード

// 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日分ほどハマった現場よりの報告でした。