Skip to content

Commit

Permalink
Introduce queueMicrotask API
Browse files Browse the repository at this point in the history
Summary:
Changelog:
[General][Added] - Add global.queueMicrotask

`queueMicrotask` is a relatively recent API defined in the WHATWG HTML spec
and it's been widely adopted by all web browsers and Node.js.

This diff introduced it to React Native by polyfilling it via a lazily-allocated
resolved Promise, or calling directly into a fast path provided by Hermes.

Reviewed By: RSNara

Differential Revision: D29838852

fbshipit-source-id: 8c4378b1b713fb8b0da5e67f92ba2ea9838766f7
  • Loading branch information
Huxpro authored and facebook-github-bot committed Jul 28, 2021
1 parent 3081db2 commit be189cd
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
41 changes: 41 additions & 0 deletions Libraries/Core/Timers/queueMicrotask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/

'use strict';

let resolvedPromise;

/**
* Polyfill for the microtask queuening API defined by WHATWG HTMP spec.
* https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-queuemicrotask
*
* The method must queue a microtask to invoke @param {function} callback, and
* if the callback throws an exception, report the exception.
*/
export default function queueMicrotask(callback: Function) {
if (arguments.length < 1) {
throw new TypeError(
'queueMicrotask must be called with at least one argument (a function to call)',
);
}
if (typeof callback !== 'function') {
throw new TypeError('The argument to queueMicrotask must be a function.');
}

// Try to reuse a lazily allocated resolved promise from closure.
(resolvedPromise || (resolvedPromise = Promise.resolve()))
.then(callback)
.catch(error =>
// Report the exception until the next tick.
setTimeout(() => {
throw error;
}, 0),
);
}
30 changes: 28 additions & 2 deletions Libraries/Core/setUpTimers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@

'use strict';

const {polyfillGlobal} = require('../Utilities/PolyfillFunctions');

if (__DEV__) {
if (typeof global.Promise !== 'function') {
console.error('Promise should exist before setting up timers.');
}
}

// Currently, Hermes `Promise` is implemented via Internal Bytecode.
const hasHermesPromiseQueuedToJSVM =
global?.HermesInternal?.hasPromise?.() &&
global?.HermesInternal?.useEngineQueue?.();

// In bridgeless mode, timers are host functions installed from cpp.
if (!global.RN$Bridgeless) {
const {polyfillGlobal} = require('../Utilities/PolyfillFunctions');

/**
* Set up timers.
* You can use this module directly, or just require InitializeCore.
Expand Down Expand Up @@ -42,3 +53,18 @@ if (!global.RN$Bridgeless) {
() => require('./Timers/JSTimers').clearReactNativeMicrotask,
);
}

/**
* Set up the microtask queueing API, which is required to use the same
* microtask queue as the Promise.
*/
if (hasHermesPromiseQueuedToJSVM) {
// Fast path for Hermes.
polyfillGlobal('queueMicrotask', () => global.HermesInternal.enqueueJob);
} else {
// Polyfill it with promise (regardless it's polyfiled or native) otherwise.
polyfillGlobal(
'queueMicrotask',
() => require('./Timers/queueMicrotask.js').default,
);
}

0 comments on commit be189cd

Please sign in to comment.