Skip to content

Don't require action within a Promise reaction microtask. #570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 19, 2025
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,40 @@ See also:

* <a href="https://www.w3.org/2001/tag/doc/promises-guide">Writing Promise-Using Specifications</a>

<h4 id="sync-callbacks">When to use callback functions instead</h4>

<p>If an algorithm requires that callback functions respond
synchronously (e.g., to take or veto some action), then that is a
synchronous API, and should not be modeled with a Promise. Instead,
use an explicit callback or event.</p>

<p>In [Promise reaction callbacks](https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promisereaction-records),
you may not require action within
a fixed number of microtasks.</p>

<p>This protects Promise composition: patterns like <code>await</code>,
wrapping helper functions, logging, <code>Promise.race()</code>, or multiple
<code>.then()</code> branches all queue an extra microtask and must
not significantly change the behavior of the code they wrap.</p>

<div class=example>
If developer-supplied code needs to be able to call <code>event.{{Event/preventDefault()}}</code>
or choose a response object with
<code>fetchEvent.{{FetchEvent/respondWith()}}</code>,
it needs to be a callback rather than a reaction passed to <code>Promise.{{Promise/then()}}</code>.
Comment on lines +2002 to +2005
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a good example or a bad one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably ingrained at this point. Whether it's a good pattern or not might be debatable. The last place I personally encountered it was https://w3c.github.io/webrtc-extensions/#dfn-icecandidatepairremove (though our motivation there was finding a manner of interfering with the ICE agent that the most people could stomach).

</div>
<div class="example">
A non-event example is the <code>captureController.{{CaptureController/setFocusBehavior()}}</code>
method, which pushes a window the user has chosen to screen-capture to
the front. Applications can decide this based on the window chosen.
But any delay risks click-jacking.

Instead of requiring the method be called synchronously on the
reaction microtask of the promise from `getDisplayMedia()`, the
solution was to instead require the method be called on the
same promise reaction task, with a one second timeout.
</div>

<h3 id="aborting">Cancel asynchronous APIs/operations using AbortSignal</h3>

If an asynchronous method can be cancelled,
Expand Down Expand Up @@ -2132,6 +2166,10 @@ Follow the <a href="https://www.w3.org/2001/tag/doc/promises-guide#one-time-even
in the <strong><a href="https://www.w3.org/2001/tag/doc/promises-guide">Writing
Promise-Using Specifications</a></strong> guideline.

<h3 id="promises-and-reaction">Don't use promises for cancelable events</h3>

See <a href="#sync-callbacks">when to use callback functions instead</a>.

<h3 id="promises-and-events">Events should fire before related Promises resolve</h3>

If a Promise-based asynchronous algorithm dispatches events,
Expand Down