* 仕様書

[REFS[
- [54] [CITE[ECMAScript® 2018 Language Specification]] ([TIME[2017-06-14 04:16:40 +09:00]]) <https://tc39.github.io/ecma262/#sec-host-promise-rejection-tracker>
- [24] [CITE[ECMAScript® 2018 Language Specification]] ([TIME[2017-06-14 04:16:40 +09:00]]) <https://tc39.github.io/ecma262/#sec-properties-of-promise-instances>
- [2] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-12-03 22:48:19 +09:00]] 版) <https://html.spec.whatwg.org/#environment-settings-object>
- [5] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-12-03 22:48:19 +09:00]] 版) <https://html.spec.whatwg.org/#unhandled-promise-rejections>
]REFS]

* イベント

[49] [[約束]]を扱う場合、[[履行]]時の処理を記述するのは普通のことで基本ですが、
[[拒絶]]時の処理は怠りがちです。 ([[例外]]が[[投げ]]られた処理を怠りがちなのと同じです。)

真に異常で通常なら起こり得ない問題のため[[拒絶]]時の処理を敢えて省くこともあれば、
失敗することもあるので[[拒絶]]時の処理を指定しなければならないのに漏れてしまった場合もあります。

どちらの場合でも、せめて[[開発者コンソール]]では気づきたいですし、
[[例外]]に対する [CODE[error][onerror]] [[イベント]]のように[[スクリプト]]側で検知したいこともあります。

[50] そのため、[[約束]]が[[拒絶]]されて処理されていないことを通知する[[イベント]]が用意されています。

-*-*-

[37] [[約束]]が[[拒絶]]され、それが処理されないままの時は、
[DFN[[CODE(DOMe)@en[[[unhandledrejection]]]]]] 
[[イベント]]が[[大域オブジェクト]]で[[発火]]されます。

;; [47] この[[イベント]]は、[[例外]]における [CODE[error][onerror]]
[[イベント]]に相当するものです。

[53] この[[イベント]]は[F[取消可能]]です。[[取消]]された場合、
[[開発者コンソール]]には何も表示されません。
[[取消]]されていない場合、[[拒絶]]について[[開発者コンソール]]に表示されます。

-*-*-

[38] [CODE[unhandledrejection]] [[イベント]]が[[発火]]された後から処理が追加された場合には、
[DFN[[CODE(DOMe)@en[[[rejectionhandled]]]]]]
[[イベント]]が[[大域オブジェクト]]で[[発火]]されます。
[CODE[unhandledrejection]] [[イベント]]の[[イベントハンドラー]]で処理が追加された場合にはこの[[イベント]]は[[発火]]されませんが、
それよりも後に処理が追加された場合に、[[発火]]されます。

;; [52] [CODE[unhandledrejection]] を[[発火]]するかどうかの判定は、
[[約束]]が[[拒絶]]されてからわりとすぐに
([[マイクロタスク]]処理後に) 行われます。しかし[[約束]]をずっと保持しておいて、
しばらく経ってから処理が追加される可能性は残ります。
その場合に [CODE[unhandledrejection]] [[イベント]]で把握できます。
ただし [CODE[unhandledrejection]] [[イベント]]が[[発火]]されることは保証されません。

-*-*-

[42] [DFN[[CODE(DOMi)@en[[[PromiseRejectionEvent]]]]]] [[インターフェイス]] [SRC[>>5]] は、
[CODE(DOMe)@en[[[rejectionhandled]]]]/[CODE(DOMe)@en[[[unhandledrejection]]]]
[[イベント]]と共に用いられます。
[[文書環境]]と[[ワーカー環境]]に[[晒され]]ています [SRC[>>5]]。

[44] [CODE(DOMi)@en[[[PromiseRejectionEvent]]]] [[インターフェイス]]は、
他の [CODE(DOMi)@en[[[Event]]]] [[インターフェイス]]同様の[[コンストラクター]]を持ちます
[SRC[>>5]]。
初期化のための[[辞書]]は [DFN[[CODE(DOMi)@en[[[PromiseRejectionEventInit]]]]]] です [SRC[>>5]]。

[43] [CODE(DOMi)@en[[[PromiseRejectionEvent]]]] [[インターフェイス]]は、
[CODE(DOMi)@en[[[Event]]]] [[インターフェイス]]を[[継承]]しています [SRC[>>5]]。
[CODE(DOMi)@en[[[PromiseRejectionEventInit]]]] [[辞書]]は、
[CODE(DOMi)@en[[[EventInit]]]] [[辞書]]を[[継承]]しています [SRC[>>5]]。

[34] [CODE(DOMi)@en[[[PromiseRejectionEvent]]]] [[インターフェイス]]の
[DFN[[CODE(DOMa)@en[[[promise]]]]]] [[IDL属性]]は、
当該通知の[[約束]]を表すもので、任意の [CODE(IDL)@en[[[Promise]]]]
を値として持つ[[読み取り専用属性]]です。
[[取得器]]は本[[イベントオブジェクト]]について指定された値を返さなければ[['''なりません''']]。 [SRC[>>5]]

[36] [CODE(DOMi)@en[[[PromiseRejectionEvent]]]] [[インターフェイス]]の
[DFN[[CODE(DOMa)@en[[[reason]]]]]] [[IDL属性]]は、
当該[[約束]]の[[拒絶]]理由を表すもので、任意値を持つ[[読み取り専用属性]]です。
[[取得器]]は本[[イベントオブジェクト]]について指定された値を返さなければ[['''なりません''']]。 [SRC[>>5]]

;; [35] つまり、 [CODE(JS)@en[[[catch]]]] で指定された[[関数]]が[[引数]]として受け取るであろう値となります。

* 処理

** 約束

[25] 
[[約束]]は、[[内部スロット]]
[DFN[[F(ss)[PromiseIsHandled]]]] を持ちます [SRC[>>24]]。

[28] この値は、 [CODE[Promise]] [[コンストラクター]]で[[偽]]に初期化されます。

[32] 
[CODE[then]] や [CODE[await]] で[[真]]に設定されます。

[64] 
[CODE[ReadableStream]] や [CODE[WritableStream]] の処理でも[[真]]に設定されることがあります。


[39] この値が[[偽]]の時に、
[CODE[HostPromiseRejectionTracker]] が実行されます。

-*-*-

[55] 
[DFN[[CODE[HostPromiseRejectionTracker]]]] は、 [[ホスト環境]]定義の内部操作として
[[JavaScript]] の[[仕様書]]で定義されているものです [SRC[>>54]]。

[56] この操作は、[[引数]]として[VAR[約束]]と[VAR[操作]]を与えられます [SRC[>>54]]。
[VAR[操作]]の値は、 [CODE[reject][HostPromiseRejectionTracker]] または 
[CODE[handle][HostPromiseRejectionTracker]] のいずれかです [SRC[>>54]]。

-*-*-

[59] 
[VAR[操作]] [DFN[[CODE[reject][HostPromiseRejectionTracker]]]]
は、[[約束]][VAR[約束]]が[[ハンドラー]]の登録されていない状態で[[拒絶]]された時、
実行されます [SRC[>>54]]。

[62] [[Web]] では、次のようにしなければ[MUST[なりません]] [SRC[>>5]]。

[FIG(steps)[
= [21] [[走っているスクリプト]]の[F[設定群オブジェクト]]の[F[これから通知する拒絶された約束リスト]]に、[VAR[約束]]を追加します。
]FIG]

[51] 
この処理は[[拒絶]]時に [F(ss)[PromiseIsHandled]] が[[偽]]の場合に、実行されます。
[[拒絶]]は1回しか起こらないため、この処理も1回しか呼び出されません。
[VAR[操作]]が [CODE[handle][HostPromiseRejectionTracker]] で呼び出された後にこちらの処理が呼び出されることもありません。

-*-*-

[60] 
[VAR[操作]] [DFN[[CODE[handle][HostPromiseRejectionTracker]]]] は、
[[約束]][VAR[約束]]が[[拒絶]]されているところ、[[ハンドラー]]が初めて追加された時、
実行されます [SRC[>>54]]。

[20] [[Web]] では、 次のようにしなければ[MUST[なりません]] [SRC[>>5]]。

[FIG(steps)[
= [22] [VAR[設定群オブジェクト]]を、[[走っているスクリプト]]の[F[[[設定群オブジェクト]]]]に設定します。
= [26] [VAR[設定群オブジェクト]]の[F[[[これから通知する拒絶された約束リスト]]]]に[VAR[約束]]が含まれている場合、
== [27] [VAR[設定群オブジェクト]]の[F[[[これから通知する拒絶された約束リスト]]]]から[VAR[約束]]を削除します。
= [29] それ以外で、[VAR[設定群オブジェクト]]の[F[[[未決拒絶された約束弱集合]]]]に[VAR[約束]]が含まれている場合、
== [30] [VAR[設定群オブジェクト]]の[F[[[未決拒絶された約束弱集合]]]]から[VAR[約束]]を削除します。
== [31] [[タスクをキューに追加]]します。
[FIG(list members)[

:[VAR[処理]]:
[FIG(steps)[
= [33] [[イベントを発火]]します。
[FIG(list members)[

:[F[対象][イベント対象]]: [VAR[設定群オブジェクト]]の[F[[[大域オブジェクト]]]]
:[F[インターフェイス][イベントインターフェイス]]: [CODE(DOMi)@en[[[PromiseRejectionEvent]]]]
:[F[型][イベント型]]: [CODE(DOMe)@en[[[rejectionhandled]]]]
:[F[[[trusted]]]]: [[真]]
:[F[[[bubbles]]]]: [[偽]]
:[F[[[取消可能]]]]: [[偽]]
:[F[[CODE(DOMa)@en[[[promise]]]]]]: [VAR[約束]]
:[F[[CODE(DOMa)@en[[[reason]]]]]]: [VAR[約束]] の [F(ss)[[[PromiseResult]]]] の値

]FIG]
]FIG]
:[VAR[[[タスク源]]]]: 
[[DOM操作タスク源]]

]FIG]

]FIG]
]FIG]

[63] 
この処理は [CODE[then]] から呼び出されます。
[F(ss)[PromiseIsHandled]] が[[偽]]の時に呼び出されますが、
本処理の直後に[[真]]に設定されるため、
本処理が呼び出されるのは高々1回です。
本処理が呼び出されるのは既に[[拒絶]]されている場合のみですから、
必ず[VAR[操作]] [CODE[reject][HostPromiseRejectionTracker]]
で1回呼び出された後になります。

** 環境設定群オブジェクト

[4] [[環境設定群オブジェクト]]は、
[DFN[[F[[RUBYB[これから通知する拒絶された約束リスト]@en[about-to-be-notified rejected promises list]]]]]]を持ちます
[SRC[>>2]]。
[[約束]]が[[拒絶]]され、処理が追加されていない場合、このリストに[[約束]]が追加されていきます。
処理が追加されると、このリストから[[約束]]は削除されます。
最終的に残った[[約束]]について、 [CODE[unhandledrejection]]
[[イベント]]が[[発火]]されます。

[3] [[環境設定群オブジェクト]]は、
[DFN[[F[[RUBYB[未決拒絶された約束弱集合]@en[outstanding rejected promises weak set]]]]]]を持ちます
[SRC[>>2]]。この[[弱集合]]からその[[構成要素]]への[[参照]]は、
[[強参照]]であっては[['''なりません''']] [SRC[>>2]]。
実装はサイズを制限しても構いません (例えば溢れるなら古いものを削除して構いません) [SRC[>>2]]。
[CODE[unhandledrejection]] [[イベント]]が[[発火]]された[[約束]]は、
[[イベントハンドラー]]でも処理が追加されないままの場合、
このリストに追加されます。
処理が追加された時、このリストに[[約束]]が含まれていれば、
[CODE[rejectionhandled]] [[イベント]]が[[発火]]されます。

[6] [[環境設定群オブジェクト]][VAR[設定群オブジェクト]]の[DFN[[RUBYB[拒絶された約束について通知]@en[notify about rejected promises]]]]は、
次のようにしなければ[['''なりません''']] [SRC[>>5]]。
[FIG(steps)[
= [7] [VAR[リスト]]を、[VAR[設定群オブジェクト]]の[F[[[これから通知する拒絶された約束リスト]]]]の複製に設定します。
= [9] [VAR[リスト]]が空なら、ここで停止します。
= [8] [VAR[設定群オブジェクト]]の[F[[[これから通知する拒絶された約束リスト]]]]を、空にします。
= [10] 次のような[[タスク]]を[[タスクキューに追加]]します。
[FIG(list members)[
[FIGCAPTION[
[11] [[タスク]]
]FIGCAPTION]
:[F[[[タスク源]]]]: ?
:[F[処理]]:
[FIG(steps)[
= [12] [VAR[リスト]]内の各[[約束]] [VAR[p]] について、
== [13] [VAR[p]] の [F(ss)[[[PromiseIsHandled]]]] が[[真]]なら、
次の[[約束]]に進みます。
== [14] 新しい[[イベント]][VAR[イベント]]を[[発火]]します。
[FIG(list members)[
[FIGCAPTION[
[15] [[イベント]]
]FIGCAPTION]
:[F[[[イベント型]]]]: [CODE(DOMe)@en[[[unhandledrejection]]]]
:[F[[[インターフェイス]]]]: [CODE(DOMi)@en[[[PromiseRejectionEvent]]]]
:[F[[[trusted]]]]: [[真]]
:[F[[[bubbles]]]]: [[偽]]
:[F[[[取消可能]]]]: [[真]]
:[F[[CODE(DOMa)@en[[[promise]]]]]]: [VAR[p]]
:[F[[CODE(DOMa)@en[[[reason]]]]]]: [VAR[p]] の [F(ss)[[[PromiseResult]]]] の値
:[F[[[対象]]]]: [VAR[設定群オブジェクト]]の[F[[[大域オブジェクト]]]]
]FIG]
== [16] 
[VAR[結果]]を、[VAR[イベント]]の[F[取消]]が[[真]]なら
「[DFN[[RUBYB[取り扱われた]@en[handled]]]]」、
[[偽]]なら「[DFN[[RUBYB[取り扱われなかった]@en[not handled]]]]」
に設定します。
;; [[エラーの報告]]も参照。
== [19] 
[VAR[結果]]が「取り扱われなかった」の場合、
=== [23] [[拒絶]]について[[開発者コンソール]]に報告して構いません。
== [17] [VAR[p]] の [F(ss)[[[PromiseIsHandled]]]] が[[偽]]なら、
=== [18] [VAR[設定群オブジェクト]]の[F[[[未決拒絶された約束弱集合]]]]に [VAR[p]]
を追加します。
]FIG]
]FIG]
]FIG]

[48] この処理は、[[マイクロタスクチェックポイントを行う]]処理から呼び出されます。

* 歴史

[43] [CITE[IRC logs: freenode / #whatwg / 20140912]]
( ([TIME[2014-09-13 11:03:22 +09:00]] 版))
<http://krijnhoetmer.nl/irc-logs/whatwg/20140912#l-688>

[44] [CITE@en[''''''[''''''whatwg'''''']'''''' An API for unhandled promise rejections]]
( ([[Domenic Denicola]] 著, [TIME[2014-09-13 03:34:23 +09:00]] 版))
<http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Sep/0024.html>

[57] [CITE@en[domenic/unhandled-rejections-browser-spec]]
([TIME[2015-06-18 08:21:28 +09:00]] 版)
<https://github.com/domenic/unhandled-rejections-browser-spec>

[58] [CITE@en[1179244 – Dispatch events for promises with unhandled rejections]]
([TIME[2015-07-02 10:30:56 +09:00]] 版)
<https://bugzilla.mozilla.org/show_bug.cgi?id=1179244>

[61] [CITE@en[Bug 150358 – Support for promise rejection events]]
([TIME[2015-10-21 11:24:26 +09:00]] 版)
<https://bugs.webkit.org/show_bug.cgi?id=150358>

[1] [CITE@en[Add promise rejection tracking events · whatwg/html@61ccc05]] ([TIME[2015-12-03 22:55:13 +09:00]] 版) <https://github.com/whatwg/html/commit/61ccc05b7437ba947390928f9e526da49550fed0>

[40] [CITE@en[Rewrite script execution on top of ES · whatwg/html@4891d18]]
([TIME[2015-12-22 23:56:19 +09:00]] 版)
<https://github.com/whatwg/html/commit/4891d18aaf2df1d40aa61f467a5a10cfc19dd85d>

[41] [CITE@en[Restore definition of "Reject" lost in 079cbb861a99e9e857a3f2a169c0be… · heycam/webidl@326291e]]
([TIME[2016-02-16 11:03:14 +09:00]] 版)
<https://github.com/heycam/webidl/commit/326291e58cf0b6e3677edb8c62682e41acaf7faf>

[45] [CITE@en[Sync with recent changes to the JS spec's job queue · whatwg/html@5af258f]]
([TIME[2016-03-24 21:47:31 +09:00]] 版)
<https://github.com/whatwg/html/commit/5af258f33e3e2f55eb30e611cb7aee625a8bd16a>

[46] [CITE@en[Clarify settings object, realm, and global relationships · whatwg/html@0866f1b]]
([TIME[2016-03-28 00:31:04 +09:00]] 版)
<https://github.com/whatwg/html/commit/0866f1b3f4b4ea5a99a30909e9bbe557dea0b460>

[65] [CITE@en[Fix writable stream writer manual usage example]]
([[domenic]]著, [TIME[2018-02-01 02:50:02 +09:00]])
<https://github.com/whatwg/streams/commit/3dffaaa475840161ee05d1d1e0c931046361239d>

[66] [CITE@en[Use the DOM manipulation task source for promise-related events]]
([[CYBAI]]著, [TIME[2019-03-30 00:46:38 +09:00]])
<https://github.com/whatwg/html/commit/d0feee54f85f60617811f2ea32a115e34233e606>

[67] [CITE@en["Notify about rejected promises" is missing a task source · Issue #3731 · whatwg/html]]
([TIME[2019-08-23 14:33:13 +09:00]])
<https://github.com/whatwg/html/issues/3731>

[68] [CITE@en[Use DOM manipulation task source for promise related events by CYBAI · Pull Request #4462 · whatwg/html]]
([TIME[2019-08-23 14:33:45 +09:00]])
<https://github.com/whatwg/html/pull/4462>

[69] [CITE@en[Add reference to DOM manipulation task source for unhandled promise r… by KiChjang · Pull Request #3844 · whatwg/html]]
([TIME[2019-08-23 14:34:14 +09:00]])
<https://github.com/whatwg/html/pull/3844>