UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

179 lines (169 loc) • 7.12 kB
import NativeReanimatedModule from './NativeReanimated'; import { isJest, shouldBeUseWeb } from './PlatformChecker'; import { makeShareableCloneOnUIRecursive, makeShareableCloneRecursive } from './shareables'; const IS_JEST = isJest(); const IS_NATIVE = !shouldBeUseWeb(); let _runOnUIQueue = []; export function setupMicrotasks() { 'worklet'; let microtasksQueue = []; let isExecutingMicrotasksQueue = false; global.queueMicrotask = callback => { microtasksQueue.push(callback); }; global.__callMicrotasks = () => { if (isExecutingMicrotasksQueue) { return; } try { isExecutingMicrotasksQueue = true; for (let index = 0; index < microtasksQueue.length; index += 1) { // we use classic 'for' loop because the size of the currentTasks array may change while executing some of the callbacks due to queueMicrotask calls microtasksQueue[index](); } microtasksQueue = []; global._maybeFlushUIUpdatesQueue(); } finally { isExecutingMicrotasksQueue = false; } }; } function callMicrotasksOnUIThread() { 'worklet'; global.__callMicrotasks(); } export const callMicrotasks = IS_NATIVE ? callMicrotasksOnUIThread : () => { // on web flushing is a noop as immediates are handled by the browser }; /** * Schedule a worklet to execute on the UI runtime. This method does not schedule the work immediately but instead * waits for other worklets to be scheduled within the same JS loop. It uses queueMicrotask to schedule all the worklets * at once making sure they will run within the same frame boundaries on the UI thread. */ export function runOnUI(worklet) { 'worklet'; if (__DEV__ && IS_NATIVE && _WORKLET) { throw new Error('runOnUI() cannot be called on the UI runtime. Please call the function synchronously or use `queueMicrotask` or `requestAnimationFrame` instead.'); } if (__DEV__ && IS_NATIVE && worklet.__workletHash === undefined) { throw new Error('runOnUI() can only be used on worklets'); } return function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (IS_JEST) { // Mocking time in Jest is tricky as both requestAnimationFrame and queueMicrotask // callbacks run on the same queue and can be interleaved. There is no way // to flush particular queue in Jest and the only control over mocked timers // is by using jest.advanceTimersByTime() method which advances all types // of timers including immediate and animation callbacks. Ideally we'd like // to have some way here to schedule work along with React updates, but // that's not possible, and hence in Jest environment instead of using scheduling // mechanism we just schedule the work ommiting the queue. This is ok for the // uses that we currently have but may not be ok for future tests that we write. NativeReanimatedModule.scheduleOnUI(makeShareableCloneRecursive(() => { 'worklet'; worklet(...args); })); return; } if (__DEV__) { // in DEV mode we call shareable conversion here because in case the object // can't be converted, we will get a meaningful stack-trace as opposed to the // situation when conversion is only done via microtask queue. This does not // make the app particularily less efficient as converted objects are cached // and for a given worklet the conversion only happens once. makeShareableCloneRecursive(worklet); makeShareableCloneRecursive(args); } _runOnUIQueue.push([worklet, args]); if (_runOnUIQueue.length === 1) { queueMicrotask(() => { const queue = _runOnUIQueue; _runOnUIQueue = []; NativeReanimatedModule.scheduleOnUI(makeShareableCloneRecursive(() => { 'worklet'; queue.forEach(_ref => { let [worklet, args] = _ref; worklet(...args); }); callMicrotasks(); })); }); } }; } /** * Schedule a worklet to execute on the UI runtime skipping batching mechanism. */ export function runOnUIImmediately(worklet) { 'worklet'; if (__DEV__ && IS_NATIVE && _WORKLET) { throw new Error('runOnUIImmediately() cannot be called on the UI runtime. Please call the function synchronously or use `queueMicrotask` or `requestAnimationFrame` instead.'); } if (__DEV__ && IS_NATIVE && worklet.__workletHash === undefined) { throw new Error('runOnUIImmediately() can only be used on worklets'); } return function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } NativeReanimatedModule.scheduleOnUI(makeShareableCloneRecursive(() => { 'worklet'; worklet(...args); })); }; } if (__DEV__ && IS_NATIVE) { const f = () => { 'worklet'; }; // @ts-ignore plugin if (f.__workletHash === undefined) { throw new Error('Failed to create a worklet. Did you forget to add Reanimated Babel plugin in babel.config.js? See installation docs at https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation#babel-plugin.'); } } function runWorkletOnJS(worklet) { for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { args[_key3 - 1] = arguments[_key3]; } // remote function that calls a worklet synchronously on the JS runtime worklet(...args); } export function runOnJS(fun) { 'worklet'; if (!IS_NATIVE || !_WORKLET) { // if we are already on the JS thread, we just schedule the worklet on the JS queue return function () { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } return queueMicrotask(args.length ? () => fun(...args) : fun); }; } if (fun.__workletHash) { // if `fun` is a worklet, we schedule a call of a remote function `runWorkletOnJS` // and pass the worklet as a first argument followed by original arguments return function () { for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { args[_key5] = arguments[_key5]; } return runOnJS(runWorkletOnJS)(fun, ...args); }; } if (fun.__remoteFunction) { // in development mode the function provided as `fun` throws an error message // such that when someone accidentally calls it directly on the UI runtime, they // see that they should use `runOnJS` instead. To facilitate that we put the // reference to the original remote function in the `__remoteFunction` property. fun = fun.__remoteFunction; } return function () { for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { args[_key6] = arguments[_key6]; } _scheduleOnJS(fun, args.length > 0 ? makeShareableCloneOnUIRecursive(args) : undefined); }; } //# sourceMappingURL=threads.js.map