Skip to content

Commit

Permalink
Add promise rejection tracking events
Browse files Browse the repository at this point in the history
This new feature uses a pair of events on global scopes, viz. unhandledrejection and rejectionhandled, to allow authors to track unhandled promise rejections. The specification was originally drafted at https://github.com/domenic/unhandled-rejections-browser-spec in collaboration with @jeisinger and @bzbarsky.

This feature depends on tc39/ecma262#76.
  • Loading branch information
domenic committed Nov 25, 2015
1 parent 2cdce89 commit 6c33f94
Showing 1 changed file with 214 additions and 5 deletions.
219 changes: 214 additions & 5 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -13525,7 +13525,9 @@ own thing rather than part of the extended sentence -->
<dd><code data-x="handler-window-onpagehide">onpagehide</code></dd>
<dd><code data-x="handler-window-onpageshow">onpageshow</code></dd>
<dd><code data-x="handler-window-onpopstate">onpopstate</code></dd>
<dd><code data-x="handler-window-onrejectionhandled">onrejectionhandled</code></dd>
<dd><code data-x="handler-window-onstorage">onstorage</code></dd>
<dd><code data-x="handler-window-onunhandledrejection">onunhandledrejection</code></dd>
<dd><code data-x="handler-window-onunload">onunload</code></dd>
<dt><span data-x="concept-element-dom">DOM interface</span>:</dt><!--TOPIC:DOM APIs-->
<dd>
Expand Down Expand Up @@ -86134,6 +86136,13 @@ interface <dfn>NavigatorOnLine</dfn> {

</dl>

<p>An <span>environment settings object</span> also has an <dfn>outstanding rejected promises
weak set</dfn> and an <dfn>about-to-be-notified rejected promises list</dfn>, used to track
<a href="#unhandled-promise-rejections">unhandled promise rejections</a>. The <span>outstanding
rejected promises weak set</span> must not create strong references to any of its members, and
implementations are free to limit its size, e.g. by removing old entries from it when new ones
are added.</p>

<p>The <dfn>relevant settings object for a global object</dfn> <var>o</var> is the
<span>environment settings object</span> whose <span>global object</span> is <var>o</var>.
(There is always a 1:1 mapping of global objects to environment settings objects.)</p>
Expand Down Expand Up @@ -86432,7 +86441,7 @@ interface <dfn>NavigatorOnLine</dfn> {
problematic position (line number and column number), using the <span>global object</span>
specified by the <span>environment settings object</span> as the target. If the error is still <i
data-x="concept-error-nothandled">not handled</i> after this, then the error may be reported to
the user.</p>
a developer console.</p>

</li>

Expand Down Expand Up @@ -86620,6 +86629,169 @@ dictionary <dfn>ErrorEventInit</dfn> : <span>EventInit</span> {
Where appropriate, it is set to the object representing the error (e.g. the exception object in
the case of an uncaught DOM exception).</p>

<h5>Unhandled promise rejections</h5>

<p>In addition to synchronous <a href="#runtime-script-errors">runtime script errors</a>, scripts
may experience asynchronous promise rejections, tracked via the <code
data-x="event-unhandledrejection">unhandledrejection</code> and <code
data-x="event-rejectionhandled">rejectionhandled</code> events.</p>

<p>When the user agent is to <dfn>notify about rejected promises</dfn> on a given
<span>environment settings object</span> <var>settings object</var>, it must run these steps:</p>

<ol>

<li><p>Let <var>list</var> be a copy of <var>settings object</var>'s <span>about-to-be-notified
rejected promises list</span>.</p></li>

<li><p>If <var>list</var> is empty, abort these steps.</p></li>

<li><p>Clear <var>settings object</var>'s <span>about-to-be-notified rejected promises
list</span>.</p></li>

<li>

<p><span>Queue a task</span> to run the following substep:</p>

<ol>

<li><p>For each promise <var>p</var> in <var>list</var>:</p>

<ol>

<li><p>If <var>p</var>'s [[PromiseIsHandled]] internal slot is true, continue to the next
iteration of the loop.</p></li>

<li><p>Let <var>event</var> be a new <span data-x="concept-events-trusted">trusted</span>
<code>PromiseRejectionEvent</code> object that does not bubble but is cancelable, and which
has the event name <code data-x="event-unhandledrejection">unhandledrejection</code>.</p></li>

<li><p>Initialise <var>event</var>'s <code
data-x="dom-PromiseRejectionEvent-promise">promise</code> attribute to <var>p</var>.</p></li>

<li><p>Initialise <var>event</var>'s <code
data-x="dom-PromiseRejectionEvent-reason">reason</code> attribute to the value of
<var>p</var>'s [[PromiseResult]] internal slot.</p></li>

<li><p><span>Dispatch</span> <var>event</var> at <var>settings object</var>'s <span>global
object</span>.</p></li>

<li><p>If the event was canceled, then the promise rejection is <i
data-x="concept-promise-rejection-handled">handled</i>. Otherwise, the promise rejection is
<i data-x="concept-promise-rejection-nothandled">not handled</i>.</p></li>

<li><p>If <var>p</var>'s [[PromiseIsHandled]] internal slot is false, add <var>p</var> to
<var>settings object</var>'s <span>outstanding rejected promises weak set</span>.

</ol>
</li>
</ol>
</li>

</ol>

<p>This algorithm results in promise rejections being marked as <dfn
data-x="concept-promise-rejection-handled"><i>handled</i></dfn> or <dfn
data-x="concept-promise-rejection-nothandled"><i>not handled</i></dfn>. These concepts parallel
<i data-x="concept-error-handled">handled</i> and <i data-x="concept-error-nothandled">not
handled</i> script errors. If a rejection is still <i
data-x="concept-promise-rejection-nothandled">not handled</i> after this, then the rejection may
be reported to a developer console.</p>


<h6>The HostPromiseRejectionTracker implementation</h6>

<p>ECMAScript contains an implementation-defined HostPromiseRejectionTracker(<var>promise</var>,
<var>operation</var>) abstract operation. User agents must use the following implementation:
<ref spec=ECMA262></p>

<ol>

<li>
<p>Let <var>script</var> be the <span data-x="concept-script">script</span> corresponding
to the running execution context.</p>

<div class="note">The exact mechanism for correlating ECMAScript's notion of execution contexts
and HTML's notion of <span data-x="concept-script">scripts</span> is not yet well-defined. See
<a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=25981">Bugzilla bug 25981</a> for
tentative ideas.</div>
</li>

<li><p>If <var>script</var> has <span>muted errors</span>, terminate these steps.</p></li>

<li><p>Let <var>settings object</var> be <var>script</var>'s <span>settings object</span>.</p>
</li>

<li>
<p>If <var>operation</var> is <code data-x="">"reject"</code>,

<ol>
<li><p>Add <var>promise</var> to <var>settings object</var>'s <span>about-to-be-notified
rejected promises list</span>.</p></li>
</ol>
</li>

<li>
<p>If <var>operation</var> is <code data-x="">"handle"</code>,

<ol>

<li><p>If <var>settings object</var>'s <span>about-to-be-notified rejected promises
list</span> contains <var>promise</var>, remove <var>promise</var> from that list and abort
these steps.</p></li>

<li><p>If <var>settings object</var>'s <span>outstanding rejected promises weak set</span>
does not contain <var>promise</var>, abort these steps.</p></li>

<li><p>Remove <var>promise</var> from <var>settings object</var>'s <span>outstanding rejected
promises weak set</span>.</p></li>

<li>
<p><span>Queue a task</span> to run the following steps:</p>

<ol>

<li><p>Let <var>event</var> be a new <span data-x="concept-events-trusted">trusted</span>
<code>PromiseRejectionEvent</code> object that does not bubble and is not cancelable, and
which has the event name <code
data-x="event-rejectionhandled">rejectionhandled</code>.</p></li>

<li><p>Initialise <var>event</var>'s <code
data-x="dom-PromiseRejectionEvent-promise">promise</code> attribute to
<var>promise</var>.</p></li>

<li><p>Initialise <var>event</var>'s <code
data-x="dom-PromiseRejectionEvent-reason">reason</code> attribute to the value of
<var>promise</var>'s [[PromiseResult]] internal slot.</p></li>

<li><p><span>Dispatch</span> <var>event</var> at <var>settings object</var>'s <span>global
object</span>.</p></li>

</ol>
</li>
</ol>
</li>
</ol>

<h6>The <code>PromiseRejectionEvent</code> interface</h6>

<pre class="idl">[Constructor(DOMString type, <span>PromiseRejectionEventInit</span> eventInitDict), Exposed=(Window,Worker)]
interface <dfn>PromiseRejectionEvent</dfn> : <span>Event</span> {
readonly attribute Promise&lt;any&gt; <span data-x="dom-PromiseRejectionEvent-promise">promise</span>;
readonly attribute any <span data-x="dom-PromiseRejectionEvent-reason">reason</span>;
};

dictionary <dfn>PromiseRejectionEventInit</dfn> : <span>EventInit</span> {
required Promise&lt;any&gt; promise;
any reason;
};</pre>

<p>The <dfn><code data-x="dom-PromiseRejectionEvent-promise">promise</code></dfn> attribute must
return the value it was initialised to. It represents the promise which this notification is about.</p>

<p>The <dfn><code data-x="dom-PromiseRejectionEvent-reason">reason</code></dfn> attribute must
return the value it was initialised to. It represents the rejection reason for the promise.</p>



<div w-nodev>
Expand Down Expand Up @@ -86959,8 +87131,11 @@ dictionary <dfn>ErrorEventInit</dfn> : <span>EventInit</span> {
<li><p>Remove the <span>microtask</span> run in the step above from the <span>microtask
queue</span>, and return to the <i>microtask queue handling</i> step.</p></li>

<li><p><i>Done</i>: Let the <span>performing a microtask checkpoint</span> flag be
false.</p></li>
<li><p><i>Done</i>: For each <span>environment settings object</span> whose <span>responsible
event loop</span> is this <span>event loop</span>, <span>notify about rejected promises</span>
on that <span>environment settings object</span>.</p></li>

<li><p>Let the <span>performing a microtask checkpoint</span> flag be false.</p></li>

</ol>

Expand Down Expand Up @@ -87776,7 +87951,9 @@ typedef <span>OnBeforeUnloadEventHandlerNonNull</span>? <dfn>OnBeforeUnloadEvent
<tr><td><dfn><code data-x="handler-window-onpagehide">onpagehide</code></dfn> <td> <code data-x="event-pagehide">pagehide</code> <!-- new -->
<tr><td><dfn><code data-x="handler-window-onpageshow">onpageshow</code></dfn> <td> <code data-x="event-pageshow">pageshow</code> <!-- new -->
<tr><td><dfn><code data-x="handler-window-onpopstate">onpopstate</code></dfn> <td> <code data-x="event-popstate">popstate</code> <!-- new -->
<tr><td><dfn><code data-x="handler-window-onrejectionhandled">onrejectionhandled</code></dfn> <td> <code data-x="event-rejectionhandled">rejectionhandled</code>
<tr><td><dfn><code data-x="handler-window-onstorage">onstorage</code></dfn> <td> <code data-x="event-storage">storage</code> <!-- new -->
<tr><td><dfn><code data-x="handler-window-onunhandledrejection">onunhandledrejection</code></dfn> <td> <code data-x="event-unhandledrejection">unhandledrejection</code>
<tr><td><dfn><code data-x="handler-window-onunload">onunload</code></dfn> <td> <code data-x="event-unload">unload</code> <!-- widely used -->
</table>

Expand Down Expand Up @@ -87884,7 +88061,9 @@ interface <dfn>WindowEventHandlers</dfn> {
attribute <span>EventHandler</span> <span data-x="handler-window-onpagehide">onpagehide</span>;
attribute <span>EventHandler</span> <span data-x="handler-window-onpageshow">onpageshow</span>;
attribute <span>EventHandler</span> <span data-x="handler-window-onpopstate">onpopstate</span>;
attribute <span>EventHandler</span> <span data-x="handler-window-onrejectionhandled">onrejectionhandled</span>;
attribute <span>EventHandler</span> <span data-x="handler-window-onstorage">onstorage</span>;
attribute <span>EventHandler</span> <span data-x="handler-window-onunhandledrejection">onunhandledrejection</span>;
attribute <span>EventHandler</span> <span data-x="handler-window-onunload">onunload</span>;
};</pre>

Expand Down Expand Up @@ -95164,6 +95343,8 @@ interface <dfn>WorkerGlobalScope</dfn> : <span>EventTarget</span> {
attribute <span>EventHandler</span> <span data-x="handler-WorkerGlobalScope-onlanguagechange">onlanguagechange</span>;
attribute <span>EventHandler</span> <span data-x="handler-WorkerGlobalScope-onoffline">onoffline</span>;
attribute <span>EventHandler</span> <span data-x="handler-WorkerGlobalScope-ononline">ononline</span>;
attribute <span>EventHandler</span> <span data-x="handler-WorkerGlobalScope-onrejectionhandled">onrejectionhandled</span>;
attribute <span>EventHandler</span> <span data-x="handler-WorkerGlobalScope-onunhandledrejection">onunhandledrejection</span>;
};</pre>

<p>A <code>WorkerGlobalScope</code> object has an associated <dfn data-dfn-for="WorkerGlobalScope"
Expand Down Expand Up @@ -95222,6 +95403,8 @@ interface <dfn>WorkerGlobalScope</dfn> : <span>EventTarget</span> {
<tr><td><dfn><code data-x="handler-WorkerGlobalScope-onlanguagechange">onlanguagechange</code></dfn> <td> <code data-x="event-languagechange">languagechange</code> <!-- new -->
<tr><td><dfn><code data-x="handler-WorkerGlobalScope-onoffline">onoffline</code></dfn> <td> <code data-x="event-offline">offline</code> <!-- new -->
<tr><td><dfn><code data-x="handler-WorkerGlobalScope-ononline">ononline</code></dfn> <td> <code data-x="event-online">online</code> <!-- new -->
<tr><td><dfn><code data-x="handler-WorkerGlobalScope-onrejectionhandled">onrejectionhandled</code></dfn> <td> <code data-x="event-rejectionhandled">rejectionhandled</code>
<tr><td><dfn><code data-x="handler-WorkerGlobalScope-onunhandledrejection">onunhandledrejection</code></dfn> <td> <code data-x="event-unhandledrejection">unhandledrejection</code>
</table>


Expand Down Expand Up @@ -113245,7 +113428,9 @@ if (s = prompt('What is your name?')) {
<code data-x="handler-window-onpagehide">onpagehide</code>;
<code data-x="handler-window-onpageshow">onpageshow</code>;
<code data-x="handler-window-onpopstate">onpopstate</code>;
<code data-x="handler-window-onrejectionhandled">onrejectionhandled</code>;
<code data-x="handler-window-onstorage">onstorage</code>;
<code data-x="handler-window-onunhandledrejection">onunhandledrejection</code>;
<code data-x="handler-window-onunload">onunload</code></td>
<td><code>HTMLBodyElement</code></td>
</tr>
Expand Down Expand Up @@ -116477,6 +116662,12 @@ if (s = prompt('What is your name?')) {
<td> <code data-x="event-resize">resize</code> event handler
<td> <span data-x="event handler content attributes">Event handler content attribute</span>

<tr>
<th id="ix-handler-window-onrejectionhandled"> <code data-x="">onrejectionhandled</code>
<td> <code data-x="handler-window-onrejectionhandled">body</code>
<td> <code data-x="event-rejectionhandled">rejectionhandled</code> event handler for <code>Window</code> object
<td> <span data-x="event handler content attributes">Event handler content attribute</span>

<tr>
<th id="ix-handler-onscroll"> <code data-x="">onscroll</code>
<td> <span data-x="handler-onscroll">HTML elements</span>
Expand Down Expand Up @@ -116549,6 +116740,12 @@ if (s = prompt('What is your name?')) {
<td> <code data-x="event-toggle">toggle</code> event handler
<td> <span data-x="event handler content attributes">Event handler content attribute</span>

<tr>
<th id="ix-handler-window-onunhandledrejection"> <code data-x="">onunhandledrejection</code>
<td> <code data-x="handler-window-onunhandledrejection">body</code>
<td> <code data-x="event-unhandledrejection">unhandledrejection</code> event handler for <code>Window</code> object
<td> <span data-x="event handler content attributes">Event handler content attribute</span>

<tr>
<th id="ix-handler-window-onunload"> <code data-x="">onunload</code>
<td> <code data-x="handler-window-onunload">body</code>
Expand Down Expand Up @@ -117287,6 +117484,12 @@ INSERT INTERFACES HERE
<td> <code>Document</code>
<td> Fired at the <code>Document</code> when it finishes parsing and again when all its subresources have finished loading

<tr> <!-- rejectionhandled -->
<td> <dfn><code data-x="event-rejectionhandled">rejectionhandled</code></dfn>
<td> <code>PromiseRejectionEvent</code>
<td> Global scope objects
<td> Fired at global scope objects when a previously-unhandled promise rejection becomes handled

<tr> <!-- reset -->
<td> <dfn><code data-x="event-reset">reset</code></dfn>
<td> <code>Event</code>
Expand Down Expand Up @@ -117329,6 +117532,12 @@ INSERT INTERFACES HERE
<td> <code>details</code> element
<td> Fired at <code>details</code> elements when they open or close

<tr> <!-- unhandledrejection -->
<td> <dfn><code data-x="event-unhandledrejection">unhandledrejection</code></dfn>
<td> <code>PromiseRejectionEvent</code>
<td> Global scope objects
<td> Fired at global scope objects when a promise rejection goes unhandled

<tr> <!-- unload -->
<td> <dfn><code data-x="event-unload">unload</code></dfn>
<td> <code>Event</code>
Expand Down Expand Up @@ -117645,10 +117854,10 @@ INSERT INTERFACES HERE
<dd><cite>Recommendation E.163 &mdash; Numbering Plan for The International Telephone Service</cite>, CCITT Blue Book, Fascicle II.2, pp. 128-134, November 1988.</dd>

<dt id="refsECMA262">[ECMA262]</dt>
<dd><cite><a href="https://people.mozilla.org/~jorendorff/es6-draft.html">ECMAScript Language Specification</a></cite>. ECMA.</dd>
<dd><cite><a href="https://tc39.github.io/ecma262/">ECMAScript Language Specification</a></cite>. Ecma International.</dd>

<dt id="refsECMA357">[ECMA357]</dt>
<dd>(Non-normative) <cite><a href="http://www.ecma-international.org/publications/standards/Ecma-357.htm">ECMAScript for XML (E4X) Specification</a></cite>. ECMA.</dd>
<dd>(Non-normative) <cite><a href="http://www.ecma-international.org/publications/standards/Ecma-357.htm">ECMAScript for XML (E4X) Specification</a></cite>. Ecma International.</dd>

<dt id="refsEDITING">[EDITING]</dt>
<dd><cite><a href="https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html">HTML Editing APIs</a></cite>, A. Gregor. W3C Editing APIs CG.</dd>
Expand Down

0 comments on commit 6c33f94

Please sign in to comment.