@depup/react-native-worklets 0.11.3-depup.0 → 0.12.1-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/Common/cpp/worklets/AnimationFrameQueue/AnimationFrameBatchinator.cpp +6 -3
  2. package/Common/cpp/worklets/Compat/StableApi.cpp +4 -0
  3. package/Common/cpp/worklets/Compat/StableApi.h +3 -1
  4. package/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp +26 -43
  5. package/Common/cpp/worklets/RunLoop/AsyncQueueImpl.cpp +32 -0
  6. package/Common/cpp/worklets/RunLoop/EventLoop.cpp +16 -7
  7. package/Common/cpp/worklets/RunLoop/EventLoop.h +4 -2
  8. package/Common/cpp/worklets/SharedItems/MemoryManager.cpp +2 -2
  9. package/Common/cpp/worklets/SharedItems/Serializable.h +14 -16
  10. package/Common/cpp/worklets/Tools/UIScheduler.cpp +10 -0
  11. package/Common/cpp/worklets/Tools/UIScheduler.h +3 -0
  12. package/Common/cpp/worklets/Tools/WorkletsJSIUtils.h +23 -0
  13. package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp +7 -4
  14. package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.h +2 -1
  15. package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp +60 -88
  16. package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h +335 -122
  17. package/README.md +2 -2
  18. package/android/build.gradle.kts +20 -6
  19. package/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp +10 -8
  20. package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.cpp +2 -11
  21. package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.h +1 -4
  22. package/android/src/main/java/com/swmansion/worklets/AndroidUIScheduler.kt +3 -0
  23. package/android/src/main/java/com/swmansion/worklets/ScriptBufferWrapper.kt +87 -43
  24. package/android/src/networking/com/swmansion/worklets/WorkletsModule.kt +25 -9
  25. package/android/src/no-networking/com/swmansion/worklets/WorkletsModule.kt +25 -9
  26. package/apple/worklets/apple/IOSUIScheduler.h +3 -0
  27. package/apple/worklets/apple/IOSUIScheduler.mm +6 -1
  28. package/apple/worklets/apple/ScriptLoader.h +11 -0
  29. package/apple/worklets/apple/ScriptLoader.mm +124 -0
  30. package/apple/worklets/apple/WorkletsModule.h +1 -16
  31. package/apple/worklets/apple/WorkletsModule.mm +4 -16
  32. package/changes.json +1 -1
  33. package/compatibility.json +4 -1
  34. package/lib/module/WorkletsModule/NativeWorklets.native.js +2 -2
  35. package/lib/module/WorkletsModule/NativeWorklets.native.js.map +1 -1
  36. package/lib/module/debug/jsVersion.js +1 -1
  37. package/lib/module/memory/serializable.native.js +59 -21
  38. package/lib/module/memory/serializable.native.js.map +1 -1
  39. package/lib/module/mock.js +58 -17
  40. package/lib/module/mock.js.map +1 -1
  41. package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js +1 -2
  42. package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js.map +1 -1
  43. package/lib/module/runLoop/workletRuntime/taskQueue.js +0 -1
  44. package/lib/module/runLoop/workletRuntime/taskQueue.js.map +1 -1
  45. package/lib/module/runtimes.native.js +7 -8
  46. package/lib/module/runtimes.native.js.map +1 -1
  47. package/lib/module/threads.js +35 -24
  48. package/lib/module/threads.js.map +1 -1
  49. package/lib/typescript/WorkletsModule/NativeWorklets.native.d.ts.map +1 -1
  50. package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts +1 -1
  51. package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts.map +1 -1
  52. package/lib/typescript/debug/jsVersion.d.ts +1 -1
  53. package/lib/typescript/memory/serializable.native.d.ts.map +1 -1
  54. package/lib/typescript/runLoop/uiRuntime/requestAnimationFrame.d.ts.map +1 -1
  55. package/lib/typescript/runLoop/workletRuntime/taskQueue.d.ts.map +1 -1
  56. package/lib/typescript/runtimes.native.d.ts.map +1 -1
  57. package/lib/typescript/specs/NativeWorkletsModule.d.ts +1 -1
  58. package/lib/typescript/threads.d.ts.map +1 -1
  59. package/lib/typescript/types.d.ts +38 -8
  60. package/lib/typescript/types.d.ts.map +1 -1
  61. package/package.json +14 -15
  62. package/plugin/index.js +1625 -1572
  63. package/src/WorkletsModule/NativeWorklets.native.ts +4 -2
  64. package/src/WorkletsModule/workletsModuleProxy.ts +2 -1
  65. package/src/debug/jsVersion.ts +1 -1
  66. package/src/memory/serializable.native.ts +103 -36
  67. package/src/mock.ts +85 -23
  68. package/src/runLoop/uiRuntime/requestAnimationFrame.ts +1 -2
  69. package/src/runLoop/workletRuntime/taskQueue.ts +0 -1
  70. package/src/runtimes.native.ts +12 -8
  71. package/src/threads.ts +38 -26
  72. package/src/types.ts +42 -8
  73. package/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp +0 -95
  74. package/Common/cpp/worklets/Registries/EventHandlerRegistry.h +0 -44
  75. package/Common/cpp/worklets/Tools/WorkletEventHandler.cpp +0 -34
  76. package/Common/cpp/worklets/Tools/WorkletEventHandler.h +0 -44
  77. package/jest/pluginTestUtils.ts +0 -16
@@ -270,14 +270,16 @@ See https://docs.swmansion.com/react-native-worklets/docs/guides/troubleshooting
270
270
  initializer: SerializableRef<() => void>,
271
271
  useDefaultQueue: boolean,
272
272
  customQueue: object | undefined,
273
- enableEventLoop: boolean
273
+ enableEventLoop: boolean,
274
+ enableLocking: boolean
274
275
  ) {
275
276
  return this.#workletsModuleProxy.createWorkletRuntime(
276
277
  name,
277
278
  initializer,
278
279
  useDefaultQueue,
279
280
  customQueue,
280
- enableEventLoop
281
+ enableEventLoop,
282
+ enableLocking
281
283
  );
282
284
  }
283
285
 
@@ -160,7 +160,8 @@ export interface WorkletsModuleProxy {
160
160
  initializer: SerializableRef<() => void>,
161
161
  useDefaultQueue: boolean,
162
162
  customQueue: object | undefined,
163
- enableEventLoop: boolean
163
+ enableEventLoop: boolean,
164
+ enableLocking: boolean
164
165
  ): WorkletRuntime;
165
166
 
166
167
  scheduleOnRuntime<TValue>(
@@ -5,4 +5,4 @@
5
5
  * version used to build the native part of the library in runtime. Remember to
6
6
  * keep this in sync with the version declared in `package.json`
7
7
  */
8
- export const jsVersion = '0.11.3';
8
+ export const jsVersion = '0.12.1';
@@ -89,6 +89,21 @@ const DETECT_CYCLIC_OBJECT_DEPTH_THRESHOLD = 30;
89
89
  // We use it to check if later on the function reenters with the same object
90
90
  let processedObjectAtThresholdDepth: unknown;
91
91
 
92
+ const serializationPathStack: string[] = [];
93
+
94
+ function withPathSegment<TValue>(segment: string, serialize: () => TValue) {
95
+ serializationPathStack.push(segment);
96
+ try {
97
+ return serialize();
98
+ } finally {
99
+ serializationPathStack.pop();
100
+ }
101
+ }
102
+
103
+ function formatSerializationPath(): string {
104
+ return serializationPathStack.join('');
105
+ }
106
+
92
107
  export function createSerializable<TValue>(
93
108
  value: TValue,
94
109
  shouldPersistRemote = false,
@@ -211,6 +226,17 @@ export function createSerializable<TValue>(
211
226
  }
212
227
  }
213
228
 
229
+ if (__DEV__) {
230
+ const constructorName =
231
+ (value as { constructor?: { name?: string } })?.constructor?.name ||
232
+ 'unknown';
233
+ const path = formatSerializationPath();
234
+ const location = path ? ` It was located at \`${path}\`.` : '';
235
+ console.warn(
236
+ `[Worklets] Cannot copy value of type \`${constructorName}\`.${location}`
237
+ );
238
+ }
239
+
214
240
  return cloneUndefined() as SerializableRef<TValue>;
215
241
  }
216
242
 
@@ -356,11 +382,17 @@ function cloneObjectProperties<T extends object>(
356
382
  if (key === '__initData' && clonedProps.__initData !== undefined) {
357
383
  continue;
358
384
  }
359
- clonedProps[key] = createSerializable(
360
- element,
361
- shouldPersistRemote,
362
- depth + 1
363
- );
385
+ if (__DEV__) {
386
+ clonedProps[key] = withPathSegment(`[${JSON.stringify(key)}]`, () =>
387
+ createSerializable(element, shouldPersistRemote, depth + 1)
388
+ );
389
+ } else {
390
+ clonedProps[key] = createSerializable(
391
+ element,
392
+ shouldPersistRemote,
393
+ depth + 1
394
+ );
395
+ }
364
396
  }
365
397
  return clonedProps;
366
398
  }
@@ -383,8 +415,12 @@ function cloneArray<T extends unknown[]>(
383
415
  shouldPersistRemote: boolean,
384
416
  depth: number
385
417
  ): SerializableRef<T> {
386
- const clonedElements = value.map((element) =>
387
- createSerializable(element, shouldPersistRemote, depth + 1)
418
+ const clonedElements = value.map((element, index) =>
419
+ __DEV__
420
+ ? withPathSegment(`[${index}]`, () =>
421
+ createSerializable(element, shouldPersistRemote, depth + 1)
422
+ )
423
+ : createSerializable(element, shouldPersistRemote, depth + 1)
388
424
  );
389
425
  const clone = WorkletsModule.createSerializableArray(
390
426
  clonedElements,
@@ -452,33 +488,39 @@ function cloneWorklet<TValue extends WorkletFunction>(
452
488
  // seems more elegant to handle it this way.
453
489
  delete (value as WorkletFunction).__stackDetails;
454
490
  }
455
- const clonedProps: Record<string, unknown> = cloneObjectProperties(
456
- value,
457
- shouldPersistRemote,
458
- depth
459
- );
460
- // to save on transferring static __initData field of worklet structure
461
- // we request serializable value to persist its UI counterpart. This means
462
- // that the __initData field that contains long strings representing the
463
- // worklet code, source map, and location, will always be
464
- // serialized/deserialized once.
465
- clonedProps.__initData = createSerializable(
466
- value.__initData,
467
- true,
468
- depth + 1
469
- );
491
+ const buildClone = (): SerializableRef<TValue> => {
492
+ const clonedProps: Record<string, unknown> = cloneObjectProperties(
493
+ value,
494
+ shouldPersistRemote,
495
+ depth
496
+ );
497
+ // to save on transferring static __initData field of worklet structure
498
+ // we request serializable value to persist its UI counterpart. This means
499
+ // that the __initData field that contains long strings representing the
500
+ // worklet code, source map, and location, will always be
501
+ // serialized/deserialized once.
502
+ clonedProps.__initData = __DEV__
503
+ ? withPathSegment('["__initData"]', () =>
504
+ createSerializable(value.__initData, true, depth + 1)
505
+ )
506
+ : createSerializable(value.__initData, true, depth + 1);
507
+
508
+ const clone = WorkletsModule.createSerializableWorklet(
509
+ clonedProps,
510
+ // TODO: Check after refactor if we can remove shouldPersistRemote parameter (imho it's redundant here since worklets are always persistent)
511
+ // retain all worklets
512
+ true
513
+ ) as SerializableRef<TValue>;
514
+ serializableMappingCache.set(value, clone);
515
+ serializableMappingCache.set(clone);
470
516
 
471
- const clone = WorkletsModule.createSerializableWorklet(
472
- clonedProps,
473
- // TODO: Check after refactor if we can remove shouldPersistRemote parameter (imho it's redundant here since worklets are always persistent)
474
- // retain all worklets
475
- true
476
- ) as SerializableRef<TValue>;
477
- serializableMappingCache.set(value, clone);
478
- serializableMappingCache.set(clone);
517
+ freezeObjectInDev(value);
518
+ return clone;
519
+ };
479
520
 
480
- freezeObjectInDev(value);
481
- return clone;
521
+ return __DEV__
522
+ ? withPathSegment(`<worklet ${value.name || 'anonymous'}>`, buildClone)
523
+ : buildClone();
482
524
  }
483
525
 
484
526
  /**
@@ -541,9 +583,22 @@ function cloneMap(
541
583
  ): SerializableRef<Map<unknown, unknown>> {
542
584
  const clonedKeys: unknown[] = [];
543
585
  const clonedValues: unknown[] = [];
586
+ let index = 0;
544
587
  for (const [key, element] of value.entries()) {
545
- clonedKeys.push(createSerializable(key));
546
- clonedValues.push(createSerializable(element));
588
+ if (__DEV__) {
589
+ clonedKeys.push(
590
+ withPathSegment(`.keys()[${index}]`, () => createSerializable(key))
591
+ );
592
+ clonedValues.push(
593
+ withPathSegment(`.values()[${index}]`, () =>
594
+ createSerializable(element)
595
+ )
596
+ );
597
+ index++;
598
+ } else {
599
+ clonedKeys.push(createSerializable(key));
600
+ clonedValues.push(createSerializable(element));
601
+ }
547
602
  }
548
603
  const clone = WorkletsModule.createSerializableMap(clonedKeys, clonedValues);
549
604
  serializableMappingCache.set(value, clone);
@@ -555,8 +610,18 @@ function cloneMap(
555
610
 
556
611
  function cloneSet(value: Set<unknown>): SerializableRef<Set<unknown>> {
557
612
  const clonedElements: unknown[] = [];
613
+ let index = 0;
558
614
  for (const element of value) {
559
- clonedElements.push(createSerializable(element));
615
+ if (__DEV__) {
616
+ clonedElements.push(
617
+ withPathSegment(`.values()[${index}]`, () =>
618
+ createSerializable(element)
619
+ )
620
+ );
621
+ index++;
622
+ } else {
623
+ clonedElements.push(createSerializable(element));
624
+ }
560
625
  }
561
626
  const clone = WorkletsModule.createSerializableSet(clonedElements);
562
627
  serializableMappingCache.set(value, clone);
@@ -638,7 +703,9 @@ function cloneCustom<TValue extends object, TPacked = unknown>(
638
703
  typeId: number
639
704
  ): SerializableRef<TValue> {
640
705
  const packedData = pack(data);
641
- const serialized = createSerializable(packedData);
706
+ const serialized = __DEV__
707
+ ? withPathSegment('.pack()', () => createSerializable(packedData))
708
+ : createSerializable(packedData);
642
709
 
643
710
  return WorkletsModule.createCustomSerializable(
644
711
  serialized,
package/src/mock.ts CHANGED
@@ -6,6 +6,8 @@ import { isWorkletFunction } from './workletFunction';
6
6
 
7
7
  const NOOP = () => {};
8
8
  const NOOP_FACTORY = () => NOOP;
9
+ const UI_RUNTIME_HOLDER = {};
10
+ const UI_SCHEDULER_HOLDER = {};
9
11
  const ID = <TValue>(value: TValue) => value;
10
12
  const IMMEDIATE_CALLBACK_INVOCATION = <TCallback>(callback: () => TCallback) =>
11
13
  callback();
@@ -23,32 +25,50 @@ globalThis._getAnimationTimestamp = () => performance.now();
23
25
  globalThis.requestAnimationFrame = mockedRequestAnimationFrame;
24
26
 
25
27
  const WorkletAPI = {
28
+ callMicrotasks: NOOP,
29
+ createSerializable: ID,
30
+ createShareable<TValue>(_hostRuntimeId: number, initial: TValue) {
31
+ let value = initial;
32
+ const set = (next: TValue | ((prev: TValue) => TValue)) => {
33
+ value =
34
+ typeof next === 'function'
35
+ ? (next as (prev: TValue) => TValue)(value)
36
+ : next;
37
+ };
38
+ return {
39
+ isHost: false,
40
+ __shareableRef: true,
41
+ getAsync: () => Promise.resolve(value),
42
+ getSync: () => value,
43
+ setAsync: set,
44
+ setSync: set,
45
+ };
46
+ },
47
+ createSynchronizable: ID,
48
+ createWorkletRuntime: NOOP_FACTORY,
49
+ executeOnUIRuntimeSync: ID,
50
+ getDynamicFeatureFlag: () => false,
51
+ getRuntimeKind: () => RuntimeKind.ReactNative,
52
+ getStaticFeatureFlag: () => false,
53
+ getUIRuntimeHolder: () => UI_RUNTIME_HOLDER,
54
+ getUISchedulerHolder: () => UI_SCHEDULER_HOLDER,
55
+ isBundleModeEnabled: () => false,
56
+ isRNRuntime: () => true,
57
+ isSerializableRef: ID,
58
+ isShareable: (value: unknown) =>
59
+ typeof value === 'object' &&
60
+ value !== null &&
61
+ !!(value as Record<string, unknown>).__shareableRef,
26
62
  isShareableRef: () => true,
63
+ isSynchronizable: () => false,
64
+ isUIRuntime: () => false, // maybe it should be true?
65
+ isWorkerRuntime: () => false,
66
+ isWorkletFunction: isWorkletFunction,
67
+ isWorkletRuntime: () => false,
27
68
  makeShareable: ID,
28
69
  makeShareableCloneOnUIRecursive: ID,
29
70
  makeShareableCloneRecursive: ID,
30
- shareableMappingCache: new Map(),
31
- getStaticFeatureFlag: () => false,
32
- setDynamicFeatureFlag: NOOP,
33
- isSynchronizable: () => false,
34
- getRuntimeKind: () => RuntimeKind.ReactNative,
35
- RuntimeKind: RuntimeKind,
36
- createWorkletRuntime: NOOP_FACTORY,
37
- runOnRuntime: ID,
38
- runOnRuntimeAsync<Args extends unknown[], ReturnValue>(
39
- _workletRuntime: unknown,
40
- worklet: (...args: Args) => ReturnValue,
41
- ...args: Args
42
- ): Promise<ReturnValue> {
43
- return WorkletAPI.runOnUIAsync(worklet, ...args);
44
- },
45
- scheduleOnRuntime: IMMEDIATE_CALLBACK_INVOCATION,
46
- createSerializable: ID,
47
- isSerializableRef: ID,
48
- serializableMappingCache: new Map(),
49
- createSynchronizable: ID,
50
- callMicrotasks: NOOP,
51
- executeOnUIRuntimeSync: ID,
71
+ registerCustomSerializable: NOOP,
52
72
  runOnJS<Args extends unknown[], ReturnValue>(
53
73
  fun: (...args: Args) => ReturnValue
54
74
  ): (...args: Args) => void {
@@ -59,6 +79,35 @@ const WorkletAPI = {
59
79
  : (fun as () => ReturnValue)
60
80
  );
61
81
  },
82
+ runOnRuntime: ID,
83
+ runOnRuntimeAsync<Args extends unknown[], ReturnValue>(
84
+ _workletRuntime: unknown,
85
+ worklet: (...args: Args) => ReturnValue,
86
+ ...args: Args
87
+ ): Promise<ReturnValue> {
88
+ return WorkletAPI.runOnUIAsync(worklet, ...args);
89
+ },
90
+ runOnRuntimeAsyncWithId<Args extends unknown[], ReturnValue>(
91
+ _runtimeId: number,
92
+ worklet: (...args: Args) => ReturnValue,
93
+ ...args: Args
94
+ ): Promise<ReturnValue> {
95
+ return WorkletAPI.runOnUIAsync(worklet, ...args);
96
+ },
97
+ runOnRuntimeSync<Args extends unknown[], ReturnValue>(
98
+ _workletRuntime: unknown,
99
+ worklet: (...args: Args) => ReturnValue,
100
+ ...args: Args
101
+ ): ReturnValue {
102
+ return worklet(...args);
103
+ },
104
+ runOnRuntimeSyncWithId<Args extends unknown[], ReturnValue>(
105
+ _runtimeId: number,
106
+ worklet: (...args: Args) => ReturnValue,
107
+ ...args: Args
108
+ ): ReturnValue {
109
+ return worklet(...args);
110
+ },
62
111
  runOnUI<Args extends unknown[], ReturnValue>(
63
112
  worklet: (...args: Args) => ReturnValue
64
113
  ): (...args: Args) => void {
@@ -89,19 +138,32 @@ const WorkletAPI = {
89
138
  });
90
139
  },
91
140
  runOnUISync: IMMEDIATE_CALLBACK_INVOCATION,
141
+ RuntimeKind: RuntimeKind,
92
142
  scheduleOnRN<Args extends unknown[], ReturnValue>(
93
143
  fun: (...args: Args) => ReturnValue,
94
144
  ...args: Args
95
145
  ): void {
96
146
  WorkletAPI.runOnJS(fun)(...args);
97
147
  },
148
+ scheduleOnRuntime: IMMEDIATE_CALLBACK_INVOCATION,
149
+ scheduleOnRuntimeWithId<Args extends unknown[], ReturnValue>(
150
+ _runtimeId: number,
151
+ worklet: (...args: Args) => ReturnValue,
152
+ ...args: Args
153
+ ): void {
154
+ WorkletAPI.scheduleOnUI(worklet, ...args);
155
+ },
98
156
  scheduleOnUI<Args extends unknown[], ReturnValue>(
99
157
  worklet: (...args: Args) => ReturnValue,
100
158
  ...args: Args
101
159
  ): void {
102
160
  WorkletAPI.runOnUI(worklet)(...args);
103
161
  },
104
- isWorkletFunction: isWorkletFunction,
162
+ serializableMappingCache: new Map(),
163
+ setDynamicFeatureFlag: NOOP,
164
+ shareableMappingCache: new Map(),
165
+ toggleSlowAnimationsOnUIRuntime: () => false,
166
+ UIRuntimeId: RuntimeKind.UI,
105
167
  WorkletsModule: {},
106
168
  };
107
169
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  export function setupRequestAnimationFrame() {
4
4
  'worklet';
5
- const callMicrotasks = globalThis.__callMicrotasks;
6
5
 
7
6
  let queuedCallbacks: ((timestamp: number) => void)[] = [];
8
7
  let queuedCallbacksBegin = 0;
@@ -28,7 +27,7 @@ export function setupRequestAnimationFrame() {
28
27
 
29
28
  flushedCallbacksBegin = flushedCallbacksEnd;
30
29
 
31
- callMicrotasks();
30
+ globalThis.__callMicrotasks();
32
31
 
33
32
  const finalizers = queuedFinalizers;
34
33
  queuedFinalizers = [];
@@ -19,7 +19,6 @@ export function setupTaskQueue() {
19
19
  const task = queue.timeoutCallbacks.get(handlerId);
20
20
  task?.();
21
21
  queue.timeoutCallbacks.delete(handlerId);
22
- globalThis.__callMicrotasks();
23
22
  };
24
23
 
25
24
  globalThis.__callMicrotasks = function callMicrotasks() {
@@ -81,6 +81,7 @@ export function createWorkletRuntime(
81
81
  let queue: 'default' | object | null = 'default';
82
82
  let animationQueuePollingRate: number;
83
83
  let enableEventLoop = true;
84
+ let enableLocking = true;
84
85
  if (typeof nameOrConfig === 'string') {
85
86
  name = nameOrConfig;
86
87
  initializerFn = initializer;
@@ -96,7 +97,15 @@ export function createWorkletRuntime(
96
97
  animationQueuePollingRate = Math.round(
97
98
  nameOrConfig?.animationQueuePollingRate ?? 16
98
99
  );
99
- enableEventLoop = nameOrConfig?.enableEventLoop ?? true;
100
+ enableLocking = nameOrConfig?.enableLocking ?? true;
101
+ if (__DEV__ && !enableLocking && nameOrConfig?.enableEventLoop) {
102
+ throw new Error(
103
+ '[Worklets] The Event Loop cannot be enabled on a runtime with locking disabled.'
104
+ );
105
+ }
106
+ enableEventLoop = enableLocking
107
+ ? (nameOrConfig?.enableEventLoop ?? true)
108
+ : false;
100
109
  }
101
110
 
102
111
  const useDefaultQueue = queue === 'default';
@@ -126,7 +135,8 @@ export function createWorkletRuntime(
126
135
  }),
127
136
  useDefaultQueue,
128
137
  customQueue,
129
- enableEventLoop
138
+ enableEventLoop,
139
+ enableLocking
130
140
  );
131
141
  }
132
142
 
@@ -172,7 +182,6 @@ export function scheduleOnRuntime<Args extends unknown[], ReturnValue>(
172
182
  createSerializable(() => {
173
183
  'worklet';
174
184
  worklet(...args);
175
- globalThis.__callMicrotasks?.();
176
185
  }),
177
186
  SHOULD_CAPTURE_SCHEDULE_STACK ? new Error().stack : undefined
178
187
  );
@@ -196,7 +205,6 @@ if (!isBundleModeEnabled()) {
196
205
  globalThis.__serializer(() => {
197
206
  'worklet';
198
207
  worklet(...args);
199
- globalThis.__callMicrotasks?.();
200
208
  })
201
209
  );
202
210
  }
@@ -250,7 +258,6 @@ export function scheduleOnRuntimeWithId<Args extends unknown[], ReturnValue>(
250
258
  createSerializable(() => {
251
259
  'worklet';
252
260
  worklet(...args);
253
- globalThis.__callMicrotasks?.();
254
261
  }),
255
262
  SHOULD_CAPTURE_SCHEDULE_STACK ? new Error().stack : undefined
256
263
  );
@@ -274,7 +281,6 @@ if (!isBundleModeEnabled()) {
274
281
  globalThis.__serializer(() => {
275
282
  'worklet';
276
283
  worklet(...args);
277
- globalThis.__callMicrotasks?.();
278
284
  })
279
285
  );
280
286
  }
@@ -470,7 +476,6 @@ export function runOnRuntimeAsync<Args extends unknown[], ReturnValue>(
470
476
  serializedError
471
477
  );
472
478
  }
473
- globalThis.__callMicrotasks?.();
474
479
  }),
475
480
  scheduleStack
476
481
  );
@@ -545,7 +550,6 @@ export function runOnRuntimeAsyncWithId<Args extends unknown[], ReturnValue>(
545
550
  serializedError
546
551
  );
547
552
  }
548
- globalThis.__callMicrotasks?.();
549
553
  }),
550
554
  scheduleStack
551
555
  );
package/src/threads.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  import { IS_JEST } from './platformChecker';
4
- import { mockedRequestAnimationFrame } from './runLoop/uiRuntime/mockedRequestAnimationFrame';
5
4
 
6
5
  export function scheduleOnUI<Args extends unknown[], ReturnValue>(
7
6
  worklet: (...args: Args) => ReturnValue,
@@ -58,8 +57,8 @@ export function runOnUIAsync<Args extends unknown[], ReturnValue>(
58
57
  worklet: (...args: Args) => ReturnValue,
59
58
  ...args: Args
60
59
  ): Promise<ReturnValue> {
61
- return new Promise<ReturnValue>((resolve) => {
62
- enqueueUI(worklet, args, resolve);
60
+ return new Promise<ReturnValue>((resolve, reject) => {
61
+ enqueueUI(worklet, args, resolve, reject);
63
62
  });
64
63
  }
65
64
 
@@ -67,6 +66,7 @@ type UIJob<Args extends unknown[] = unknown[], ReturnValue = unknown> = [
67
66
  worklet: (...args: Args) => ReturnValue,
68
67
  args: Args,
69
68
  resolve?: (value: ReturnValue) => void,
69
+ reject?: (reason?: unknown) => void,
70
70
  ];
71
71
 
72
72
  let runOnUIQueue: UIJob[] = [];
@@ -74,37 +74,49 @@ let runOnUIQueue: UIJob[] = [];
74
74
  function enqueueUI<Args extends unknown[], ReturnValue>(
75
75
  worklet: (...args: Args) => ReturnValue,
76
76
  args: Args,
77
- resolve?: (value: ReturnValue) => void
77
+ resolve?: (value: ReturnValue) => void,
78
+ reject?: (reason?: unknown) => void
78
79
  ): void {
79
- if (IS_JEST) {
80
- mockedRequestAnimationFrame(() => {
81
- const result = worklet(...args);
82
- resolve?.(result);
83
- });
84
- } else {
85
- const job = [worklet, args, resolve];
86
- runOnUIQueue.push(job as UIJob);
87
- if (runOnUIQueue.length === 1) {
80
+ const job = [worklet, args, resolve, reject];
81
+ runOnUIQueue.push(job as UIJob);
82
+ if (runOnUIQueue.length === 1) {
83
+ if (IS_JEST) {
88
84
  flushUIQueue();
85
+ } else {
86
+ queueMicrotask(flushUIQueue);
89
87
  }
90
88
  }
91
89
  }
92
90
 
91
+ let offset = 0;
92
+
93
93
  function flushUIQueue(): void {
94
- queueMicrotask(() => {
95
- const queue = runOnUIQueue;
96
- runOnUIQueue = [];
97
- requestAnimationFrameImpl(() => {
98
- queue.forEach(([workletFunction, workletArgs, jobResolve]) => {
99
- const result = workletFunction(...workletArgs);
100
- if (jobResolve) {
101
- jobResolve(result);
94
+ const queue = runOnUIQueue;
95
+ runOnUIQueue = [];
96
+ requestAnimationFrame(() => {
97
+ offset = 0;
98
+ while (queue.length > offset) {
99
+ try {
100
+ drainUIQueue(queue);
101
+ } catch (e) {
102
+ const [, , , jobReject] = queue[offset - 1];
103
+ if (jobReject) {
104
+ jobReject(e);
105
+ } else {
106
+ console.error(e);
102
107
  }
103
- });
104
- });
108
+ }
109
+ }
105
110
  });
106
111
  }
107
112
 
108
- const requestAnimationFrameImpl = !globalThis.requestAnimationFrame
109
- ? mockedRequestAnimationFrame
110
- : globalThis.requestAnimationFrame;
113
+ function drainUIQueue(queue: UIJob[]): void {
114
+ while (queue.length > offset) {
115
+ const [workletFunction, workletArgs, jobResolve] = queue[offset];
116
+ offset++;
117
+ const result = workletFunction(...workletArgs);
118
+ if (jobResolve) {
119
+ jobResolve(result);
120
+ }
121
+ }
122
+ }
package/src/types.ts CHANGED
@@ -121,18 +121,52 @@ type WorkletRuntimeConfigBase = {
121
121
  * by requestAnimationFrame. If not specified, it defaults to 16 ms.
122
122
  */
123
123
  animationQueuePollingRate?: number;
124
- /**
125
- * Determines whether to enable the default Event Loop or not. The Event Loop
126
- * provides implementations for `setTimeout`, `setImmediate`, `setInterval`,
127
- * `requestAnimationFrame`, `queueMicrotask`, `clearTimeout`, `clearInterval`,
128
- * `clearImmediate`, and `cancelAnimationFrame` methods. If not specified, it
129
- * defaults to `true`.
130
- */
131
- enableEventLoop?: boolean;
132
124
  };
133
125
 
134
126
  /** Configuration object for creating a worklet runtime. */
135
127
  export type WorkletRuntimeConfig = WorkletRuntimeConfigBase &
128
+ (
129
+ | {
130
+ /**
131
+ * Determines whether to enable the default Event Loop, which provides
132
+ * `setTimeout`, `setInterval`, `requestAnimationFrame` and other
133
+ * scheduling APIs. If not specified, it defaults to `true`. Cannot be
134
+ * enabled when {@link enableLocking} is set to `false`.
135
+ */
136
+ enableEventLoop?: boolean;
137
+ /**
138
+ * Determines whether access to the underlying JS runtime is
139
+ * synchronized with a mutex around every JSI operation. If not
140
+ * specified, it defaults to `true`. Can be disabled only together
141
+ * with the Event Loop.
142
+ *
143
+ * Runtime without locking doesn't receive HMR updates
144
+ * and new Custom Serializable registrations.
145
+ *
146
+ * Disable only when you can guarantee thread safety of all access to the runtime.
147
+ */
148
+ enableLocking?: true;
149
+ }
150
+ | {
151
+ /**
152
+ * The Event Loop cannot be enabled on a runtime with
153
+ * {@link enableLocking} set to `false`.
154
+ */
155
+ enableEventLoop?: false;
156
+ /**
157
+ * Determines whether access to the underlying JS runtime is
158
+ * synchronized with a mutex around every JSI operation. If not
159
+ * specified, it defaults to `true`. Can be disabled only together
160
+ * with the Event Loop.
161
+ *
162
+ * Runtime without locking doesn't receive HMR updates
163
+ * and new Custom Serializable registrations.
164
+ *
165
+ * Disable only when you can guarantee thread safety of all access to the runtime.
166
+ */
167
+ enableLocking: false;
168
+ }
169
+ ) &
136
170
  (
137
171
  | {
138
172
  /**