Skip to content
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

Fix issue with setImmediate not defined on Web #4276

Merged
merged 4 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
"convert-source-map": "^2.0.0",
"invariant": "^2.2.4",
"lodash.isequal": "^4.5.0",
"setimmediate": "^1.0.5",
"string-hash-64": "^1.0.3"
},
"peerDependencies": {
Expand Down
1 change: 1 addition & 0 deletions plugin/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plugin/index.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const globals = new Set([
'UIManager',
'requestAnimationFrame',
'setImmediate',
'queueMicrotask',
'_WORKLET',
'arguments',
'Boolean',
Expand Down
8 changes: 4 additions & 4 deletions src/reanimated2/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export function registerEventHandler<T>(
eventHash: string,
eventHandler: (event: T) => void
): string {
function handleAndFlushImmediates(eventTimestamp: number, event: T) {
function handleAndFlushAnimationFrame(eventTimestamp: number, event: T) {
'worklet';
global.__frameTimestamp = eventTimestamp;
eventHandler(event);
Expand All @@ -126,7 +126,7 @@ export function registerEventHandler<T>(
}
return NativeReanimatedModule.registerEventHandler(
eventHash,
makeShareableCloneRecursive(handleAndFlushImmediates)
makeShareableCloneRecursive(handleAndFlushAnimationFrame)
);
}

Expand All @@ -140,7 +140,7 @@ export function subscribeForKeyboardEvents(
): number {
// TODO: this should really go with the same code path as other events, that is
// via registerEventHandler. For now we are copying the code from there.
function handleAndFlushImmediates(state: number, height: number) {
function handleAndFlushAnimationFrame(state: number, height: number) {
'worklet';
const now = performance.now();
global.__frameTimestamp = now;
Expand All @@ -149,7 +149,7 @@ export function subscribeForKeyboardEvents(
global.__frameTimestamp = undefined;
}
return NativeReanimatedModule.subscribeForKeyboardEvents(
makeShareableCloneRecursive(handleAndFlushImmediates),
makeShareableCloneRecursive(handleAndFlushAnimationFrame),
options.isStatusBarTranslucentAndroid ?? false
);
}
Expand Down
4 changes: 1 addition & 3 deletions src/reanimated2/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ declare global {
};
const _frameCallbackRegistry: FrameCallbackRegistryUI;
const requestAnimationFrame: (callback: (time: number) => void) => number;
const setImmediate: (callback: (time: number) => void) => number;
const console: Console;

namespace NodeJS {
Expand Down Expand Up @@ -134,10 +133,9 @@ declare global {
__workletsCache?: Map<string, (...args: any[]) => any>;
__handleCache?: WeakMap<any, any>;
__mapperRegistry?: MapperRegistry;
__flushImmediates: () => void;
__callMicrotasks: () => void;
__flushAnimationFrame: (frameTimestamp: number) => void;
requestAnimationFrame: (callback: (time: number) => void) => number;
setImmediate: (callback: (time: number) => void) => number;
console: Console;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/reanimated2/hook/useAnimatedSensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
ValueRotation,
IOSReferenceFrame,
} from '../commonTypes';
import { flushImmediates } from '../threads';
import { callMicrotasks } from '../threads';

export type SensorConfig = {
interval: number | 'auto';
Expand Down Expand Up @@ -145,7 +145,7 @@ export function useAnimatedSensor(
}
}
sensorData.value = data;
flushImmediates();
callMicrotasks();
}
);

Expand Down
8 changes: 4 additions & 4 deletions src/reanimated2/initializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import NativeReanimatedModule from './NativeReanimated';
import { isJest } from './PlatformChecker';
import {
runOnJS,
setupSetImmediate,
flushImmediates,
setupMicrotasks,
callMicrotasks,
runOnUIImmediately,
} from './threads';

Expand Down Expand Up @@ -106,7 +106,7 @@ function setupRequestAnimationFrame() {
const currentCallbacks = animationFrameCallbacks;
animationFrameCallbacks = [];
currentCallbacks.forEach((f) => f(frameTimestamp));
flushImmediates();
callMicrotasks();
};

global.requestAnimationFrame = (
Expand Down Expand Up @@ -177,7 +177,7 @@ export function initializeUIRuntime() {
};

if (!IS_JEST) {
setupSetImmediate();
setupMicrotasks();
setupRequestAnimationFrame();
}
})();
Expand Down
1 change: 0 additions & 1 deletion src/reanimated2/jestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ export const setUpTests = (userConfig = {}) => {
}
}

require('setimmediate');
frameTime = Math.round(1000 / config.fps);

config = {
Expand Down
2 changes: 1 addition & 1 deletion src/reanimated2/js-reanimated/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AnimatedStyle, StyleProps } from '../commonTypes';
const reanimatedJS = new JSReanimated();

global._makeShareableClone = (c) => c;
global._scheduleOnJS = setImmediate;
global._scheduleOnJS = queueMicrotask;

interface JSReanimatedComponent {
previousStyle: StyleProps;
Expand Down
4 changes: 2 additions & 2 deletions src/reanimated2/mappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ export function createMapperRegistry() {

function maybeRequestUpdates() {
if (IS_JEST) {
// On Jest environment we avoid using setImmediate as that'd require test
// On Jest environment we avoid using queueMicrotask as that'd require test
// to advance the clock manually. This on other hand would require tests
// to know how many times mappers need to run. As we don't want tests to
// make any assumptions on that number it is easier to execute mappers
// immediately for testing purposes and only expect test to advance timers
// if they want to make any assertions on the effects of animations being run.
mapperRun();
} else if (!runRequested) {
setImmediate(mapperRun);
queueMicrotask(mapperRun);
runRequested = true;
}
}
Expand Down
34 changes: 17 additions & 17 deletions src/reanimated2/threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,40 @@ const IS_WEB = shouldBeUseWeb();

let _runOnUIQueue: Array<[ComplexWorkletFunction<any[], any>, any[]]> = [];

export function setupSetImmediate() {
export function setupMicrotasks() {
'worklet';

let immediateCallbacks: Array<() => void> = [];
let microtasksQueue: Array<() => void> = [];

// @ts-ignore – typescript expects this to conform to NodeJS definition and expects the return value to be NodeJS.Immediate which is an object and not a number
global.setImmediate = (callback: () => void): number => {
immediateCallbacks.push(callback);
global.queueMicrotask = (callback: () => void): number => {
microtasksQueue.push(callback);
return -1;
};

global.__flushImmediates = () => {
for (let index = 0; index < immediateCallbacks.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 setImmediate calls
immediateCallbacks[index]();
global.__callMicrotasks = () => {
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]();
}
immediateCallbacks = [];
microtasksQueue = [];
};
}

function flushImmediatesOnUIThread() {
function callMicrotasksOnUIThread() {
'worklet';
global.__flushImmediates();
global.__callMicrotasks();
}

export const flushImmediates = shouldBeUseWeb()
export const callMicrotasks = shouldBeUseWeb()
? () => {
// on web flushing is a noop as immediates are handled by the browser
}
: flushImmediatesOnUIThread;
: callMicrotasksOnUIThread;

/**
* 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 setImmediate to schedule all the worklets
* 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<A extends any[], R>(
Expand All @@ -55,7 +55,7 @@ export function runOnUI<A extends any[], R>(
}
return (...args) => {
if (IS_JEST) {
// Mocking time in Jest is tricky as both requestAnimationFrame and setImmediate
// 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
Expand All @@ -74,7 +74,7 @@ export function runOnUI<A extends any[], R>(
}
_runOnUIQueue.push([worklet, args]);
if (_runOnUIQueue.length === 1) {
setImmediate(() => {
queueMicrotask(() => {
const queue = _runOnUIQueue;
_runOnUIQueue = [];
NativeReanimatedModule.scheduleOnUI(
Expand All @@ -83,7 +83,7 @@ export function runOnUI<A extends any[], R>(
queue.forEach(([worklet, args]) => {
worklet(...args);
});
flushImmediates();
callMicrotasks();
})
);
});
Expand Down
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8600,11 +8600,6 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"

setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==

setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
Expand Down