Chrome 79 から Worklet.addModule() が詳細なエラーを返すようになった話
Chrome 79 から Worklet が addModule() を失敗したときにより詳細なエラーを返すようになります。本記事はその紹介です。
今まで (Chrome 78 以前)
Worklet1 に module script を読み込むときは addModule() という関数を使います。例えば Paint Worklet の場合は次のようになります。
window.CSS.paintWorklet.addModule('worklet.js');
addModule() は promise を返します。成功した場合は undefined で resolve された promise が、失敗した場合は Error オブジェクトで reject されます。
window.CSS.paintWorklet.addModule('worklet.js')
.then(result => assert_equals(result, undefined))
.catch(error => assert_equals(error.name, 'AbortError'));
addModule() が reject される原因はいくつかあります。例えば、module script の fetch に失敗した場合や、文法ミスで parse に失敗した場合に reject されます2。
Chrome 78 以前では失敗原因に関わらず常に AbortError で reject されていました。これは実装レベルの問題ではなく仕様レベルで規定されていた挙動で、実際の失敗原因が全く通知されないためアプリケーションのデバッグを著しく困難にしていました。
これから (Chrome 79 以降)
Chrome 79 からは失敗原因に沿った Error オブジェクトで reject するようになりました。
Worklet script に文法ミスがある場合は SyntaxError で reject されます。
// syntax-error-worklet.js
1+;
// index.html
window.CSS.paintWorklet.addModule('syntax-error-worklet.js')
.then(result => assert_not_reached())
.catch(error => assert_equals(error.name, 'SyntaxError'));
Static import に指定した識別子が不適合な場合は TypeError で reject されます。
// invalid-specifier-worklet.js
import 'invalid';
// index.html
window.CSS.paintWorklet.addModule('invalid-specifier-worklet.js')
.then(result => assert_not_reached())
.catch(error => assert_equals(error.name, 'TypeError'));
Module script の fetch に失敗した場合は引き続き AbortError で reject されます。
window.CSS.paintWorklet.addModule('non-existent-worklet.js')
.then(result => assert_not_reached())
.catch(error => assert_equals(error.name, 'AbortError'));
Worklet の仕様変更はこちらとこちらで、Chrome の実装変更は crbug.com/782066 で確認できます。Chrome Platform Status のエントリもあります。
技術的な話
この問題は Worklet 実装初期から知られていましたが、ECMAScript の定義する Native Error オブジェクト (要するに SyntaxError や TypeError) が clonable ではなかったために実現できていませんでした。
Worklet はメインの実行コンテキスト3である Window から独立した実行コンテキスト WorkletGlobalScope で実行されます。Worklet の script fetch や parse は WorkletGlobalScope で行われ、Native Error オブジェクトはそれに紐付いた形で生成されます。一方、addModule() を呼んだのは Window なので、promise を reject するために Native Error オブジェクトを WorkletGlobalScope から Window へ渡す必要があります。これは JavaScript の実行コンテキストをまたぐため clonable であるべきですが、仕様的 に Native Error オブジェクトを clone することはできませんでした。
最近 yhirano さんが clonable にする変更を HTML 仕様と Chrome 実装の両方に加えたためこれが実現できるようになりました。素晴らしい!
まとめ
Chrome 79 から Worklet.addModule() がより詳細なエラーを返すようになりました。Worklet を使ったアプリケーションを開発するときに役立つはずです。
注釈
-
Worklet については「JavaScript のスレッド並列実行環境 - 6. Worklet」を参照。 ↩
-
addModule() が reject されるのは script fetch や parse の間にエラーが起きたときだけで、スクリプト評価時のエラー (たとえば dynamic import の失敗) などは addModule() を reject しません。 ↩
-
実行コンテキストについては「JavaScript のスレッド並列実行環境 - 2. Web Worker」を参照。 ↩