質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.30%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

303閲覧

await を書き忘れた場合の catch 方法

munekun

総合スコア116

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2025/06/09 11:32

編集2025/06/09 11:37

実現したいこと

JavaScript で await を忘れたときに catch したいです。

発生している問題

該当のソースコードについて、[実行➁] の場合に値が確認できなくて困りました。

[実行➀] await あり
-> ブラウザ開発ツールで {args: 'illegal'} が確認できる

[実行➁] await を忘れた
-> ブラウザ開発ツールで {args: 'illegal'} が確認できない

具体的に言いますと、[実行➁] において unhandledrejection が反応せず、await を忘れたときにブラウザ開発ツールで制御できないという問題です。

await を忘れたときも、{args: 'illegal'} を確認したいのですが、良い方法はありますか?

該当のソースコード

ポイントとしては、handle-unhandled-errors.js ファイルのwindow.onerrorunhandledrejection で制御しているつもりなのに、await を忘れたときにどちらも反応してくれない!という点です。

main.js

JavaScript

1import { initApp } from "./init-app.js"; 2import { handleError } from "./handle-error.js"; 3 4(async () => { 5 try { 6 // [実行➀] await あり 7 // -> 問題なく開発ツールで {args: 'illegal'} が確認できる 8 // await initApp(); 9 10 // [実行➁] await を忘れた 11 // -> 確認できない 12 initApp(); 13 } catch (error) { 14 handleError(error); 15 } 16})();
init-app.js

JavaScript

1import { hoge } from "./hoge.js"; 2 3export const initApp = async () => { 4 await hoge(); 5};
hoge.js

JavaScript

1import { fuga } from "./fuga.js"; 2 3export const hoge = async () => { 4 await fuga(); 5};
fuga.js

JavaScript

1import { validate } from "./validator.js"; 2 3export const fuga = async () => { 4 const args = "illegal"; // 不正な値で検証時に throw する 5 validate(args); 6};
validator.js

JavaScript

1import { CustomError } from "./CustomError.js"; 2 3export const validate = (args) => { 4 if (args === "illegal") { 5 throw new CustomError("不正な値です.", { args }); 6 } 7 8 return true; 9};
handle-error.js

JavaScript

1export const handleError = (error, options = {}) => { 2 error.logError(options); 3};
CustomError.js

JavaScript

1// 第二引数に確認用の debugInfo を持ったエラー 2export class CustomError extends Error { 3 constructor(message, debugInfo = {}) { 4 super(message); 5 this.name = "CustomError"; 6 this.debugInfo = debugInfo; 7 } 8 9 logError() { 10 if (this.hasOwnProperty("debugInfo")) { 11 console.error(this.stack, this.debugInfo); 12 } else { 13 console.error(this.stack); 14 } 15 } 16}
handle-unhandled-errors.js

JavaScript

1import { CustomError } from "./CustomError.js"; 2 3window.onerror = function (message, source, lineno, colno, error) { 4 console.log("未制御例外を処理します", { error }); 5 6 if (error instanceof CustomError) { 7 if (error.hasOwnProperty("debugInfo")) { 8 console.error("Unhandled", error.stack, error.debugInfo); 9 } else { 10 console.error("Unhandled", error.stack); 11 } 12 13 // true を返し, throw によるデフォルトのコンソール出力を防ぐ 14 return true; 15 } else { 16 // どうせ window.onerror までしかスタックを追えぬので 17 // ここはデフォルトのコンソール出力を使う 18 return false; 19 } 20}; 21 22// async など Promise の過程は onerror が使えないので addEventListener する 23window.addEventListener("unhandledrejection", (event) => { 24 const error = event.reason; 25 26 console.log("Promise系の未制御例外を処理します", { error }); 27 28 // 文字列や非Errorオブジェクトもカバーする 29 const isErrorInstance = error instanceof Error; 30 const message = isErrorInstance 31 ? error.message 32 : typeof error === "string" 33 ? error 34 : JSON.stringify(error); 35 36 const stack = 37 isErrorInstance && error.stack 38 ? error.stack 39 : "No stack trace available"; 40 41 const debugInfo = 42 isErrorInstance && "debugInfo" in error ? error.debugInfo : null; 43 44 if (debugInfo) { 45 // デフォルトのエラーハンドリングを防ぎ, 代わりに debugInfo つきの console.error を実行 46 event.preventDefault(); 47 console.error("Unhandled", stack, debugInfo); 48 } else { 49 // デフォルトのエラーハンドリングが一番情報が多いのでここは何もしない 50 } 51 52 // logErrorToServer({ 53 // message, 54 // stack, 55 // }); 56});

試したこと

下記 [実行➂] のように、initApp() の返り値を確認し、await を忘れた場合にも catch で拾うように Promise.catch を明示してみました。

main.js (修正バージョン)

JavaScript

1import { initApp } from "./init-app.js"; 2import { handleError } from "./handle-error.js"; 3 4(async () => { 5 try { 6 // [実行➀] await あり 7 // -> 問題なく開発ツールで {args: 'illegal'} が確認できる 8 // await initApp(); 9 10 // [実行➁] await を忘れた 11 // -> 確認できない 12 // initApp(); 13 14 // [実行➂] await を忘れた場合にも catch で拾うように Promise.catch を明示 15 // -> 確認できたけどこれ書くならそもそも await を書き忘れることはない 16 const result = initApp(); 17 if (result instanceof Promise) { 18 result.catch((error) => handleError(error)); 19 } 20 } catch (error) { 21 handleError(error); 22 } 23})();

すると確認できましたが、これを書くならそもそも await を書き忘れることはないでしょうからダメダメな気がいたします・・

理想としては handle-unhandled-errors.js の修正による解決で、「await を書き忘れてもいつだって僕がなんとかしてあげるよ!」みたいな頼れる感じのやつなのですが、良い方法ございますでしょうか?

補足情報(FW/ツールのバージョンなど)

Google Chrome 利用ですが、利用ブラウザに関わらず確認できるようにしたいなと思っています。

以上です。
よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lhankor_Mhy

2025/06/10 09:25

> [実行➁] await を忘れた > -> ブラウザ開発ツールで {args: 'illegal'} が確認できない というのがよくわからなかったです。コンソールに {args: 'illegal'} が表示されているだけではダメ、ということでしょうか?
munekun

2025/06/10 09:37

要領を得ない質問をいつも丁寧に読んでくださって誠にありがとうございます。 > というのがよくわからなかったです。コンソールに {args: 'illegal'} が表示されているだけではダメ、ということでしょうか? 「コンソール」とは「ブラウザ開発ツールのコンソール」ですよね?うーん、[実行➁] のように await を書き忘れたケースにおいて {args: 'illegal'} は表示されないと思います。(今改めて確認しました。) [実行➁] のように await を書き忘れたケースでも {args: 'illegal'} をコンソールに表示したいのです。でも handle-unhandled-errors.js のコードでは catch できず、コンソールに表示できないのです。なので、await を書き忘れた場合の catch 方法というタイトルで、handle-unhandled-errors.js の修正による解決を求めているという次第です。 [実行➁] のように await を書き忘れたケースでは handle-unhandled-errors.js による catch が効かず、 {args: 'illegal'} がコンソールに表示されず、ちょっと困っちゃった。という感じです。 期待していたのは「handle-unhandled-errors.js が、最後の砦として、未 catch をおしなべて catch してくれる」という機能だったのですが、今回のように await を書き忘れたケースでは catch してくれなくて、 catch してくれないから、{args: 'illegal'} がコンソールに表示されないのです。
Lhankor_Mhy

2025/06/10 09:49

もしかして、import "./handle-unhandled-errors.js"; が漏れているとか……?
munekun

2025/06/10 10:04

おそらく [実行➁] のように await を書き忘れたケースの結果が、画像の一番最後の Uncaught ... だと思います。 画像の上から2番目の Unhandled ... には {args: 'illegal'} が表示されていますよね?これを常に期待しています。 期待しているのは、[実行➁] のように await を書き忘れたケースでも、 {args: 'illegal'} を表示することです。 そのために Uncaught ...(ブラウザが自動で表示するログ)ではダメで、 Unhandled ...(handle-unhandled-errors.js による catch が表示するログ)を出したいと思っています。 なのに、現状の handle-unhandled-errors.js は、[実行➁] のように await を書き忘れたケースでは catch してくれないのです。
Lhankor_Mhy

2025/06/10 10:17

画像の上から2番目のが、[実行➁]の際に表示されているんですが…… 逆に言うと[実行①]の時にはエラーがハンドルされるので、unhandled ... とは出ないと思うんですよね。 どうも、そちらとこちらでは違うコードを実行してそうですね。
munekun

2025/06/10 10:22 編集

> もしかして、import "./handle-unhandled-errors.js"; が漏れているとか……? ここまで自分がアホだとは思いませんでした……。 仰る通りでした。泣きたい。 ありがとうございます、というか、もう本当に申し訳ございません。
Lhankor_Mhy

2025/06/10 10:26

ご解決されて何よりです。 そういう時ってありますよね、お察しいたします……
guest

回答3

0

こんにちは。

Promise 内で throw されてハンドルされなかった例外は console に自動的に出力されていると思いますが、それでは不足なのでしょうか?
await していない以上コンテキストが一致しないため catch は不可能ですが、console で観測した時点でコードを修正すれば済む話な気がしています。
あるいは、Chrome devtools の Breakpoints 設定で 'Pause on caught exceptions' のチェックを入れると、非同期例外であっても発生直後に devtools で観測できるようになります。

「await し忘れ」を問題としているのであれば、上記の対応のいずれかで開発途中に解消できるのではないかと思います。
「await していない対象の例外を catch する」をしたいというのが主題である場合、それは非同期の仕組み上根本的に不可能です。

投稿2025/06/10 08:58

tamoto

総合スコア4331

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

munekun

2025/06/10 09:14

ありがとうございます。 > それでは不足なのでしょうか? debugInfo の値、つまり{args: 'illegal'} が確認できないのが不足を感じているポイントなのです。 > console で観測した時点でコードを修正すれば済む話な気がしています。 それは一理ありだと思います。実際今回も観測した時点で修正できました。 けれど handle-unhandled-errors.js で制御できないのが納得できなないポイントなのです。 せっかく「未 catch をおしなべて制御するための handle-unhandled-errors.js を作った」のに、それでは安心しきれない(debugInfo の値が確認できないケースがある)というのが・・うーん、という感じなのです。
tamoto

2025/06/10 09:34

handle-unhandled-errors.js をインポートする ("unhandledrejection" を listen する) と、Promise の投げっぱなし例外はそちらに流れるはずなので、そこで event オブジェクトをそのまま丸ごと console.error に放り込めば console 上で debugInfo の値も確認できそうな気がします。 await し忘れの例外というものは基本的に1件も存在してはいけないと考えた方が良いので、それくらい雑に観測した方が (得られる情報量も多いので) 良い結果になるのではないかと思いました。 ```handle-unhandled-errors.js window.addEventListener("unhandledrejection", console.error); // これだけ ```
munekun

2025/06/10 10:27

ありがとうございます。自分であれやこれやとマシマシしてしまいましたが、そちらの `// これだけ` でもよかったのですね。下手なアレンジを続けずに済みました。 尚、質問の方はコメント欄で Lhankor_Mhy さんがご指摘くださったように import 忘れが原因でした。
guest

0

自己解決

コメント欄にて、Lhankor_Mhy さんがご回答くださった下記の通りでした。

もしかして、import "./handle-unhandled-errors.js"; が漏れているとか……?

くだらないミスでお騒がせしてしまい大変申し訳ございません。

Lhankor_Mhy さん、本当にありがとうございます。
tamoto さん、yambejp さんもご助力感謝です。

投稿2025/06/10 10:25

munekun

総合スコア116

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

catchしたいならerrorをthrowしてください
そうなると戻り値の型をチェックするのが手っ取り早いかと

html

1<script type="module"> 2const test=async ()=>{ 3 return true; 4}; 5(async()=>{ 6 try{ 7 const t1=test(); 8 //awaitしないとtestの戻り値はPromiseになる 9 if(t1 instanceof Promise) throw new Error("no await t1"); 10 }catch(e){ 11 console.log(e); 12 } 13})(); 14(async()=>{ 15 try{ 16 const t2=await test(); 17 //awaitしているのでtestの戻り値はreturnした値 18 if(t2 instanceof Promise) throw new Error("no await t2"); 19 }catch(e){ 20 console.log(e); 21 } 22})(); 23</script>

投稿2025/06/09 12:16

編集2025/06/10 00:11
yambejp

総合スコア117780

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

munekun

2025/06/10 08:19

ご回答ありがとうございます。要領を得ない質問で申し訳ございません。参考にさせていただきます。
yambejp

2025/06/10 08:47

ようはasync内でpromiseにawaitをつけようがつけまいがエラーにはならないのでそのままではcatchできないということです。強制的にcatchしたいときはerrorをthrowするということを理解ください
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問