Skip to content

Commit 4ba5850

Browse files
arturovtalxhub
authored andcommittedJun 30, 2023
fix(core): use setTimeout when coalescing tasks in Node.js (#50820)
This commit updates the implementation of the `getNativeRequestAnimationFrame` and checks whether the current code runs in the browser before retrieving `requestAnimationFrame`. `requestAnimationFrame` is not available when the code is running in the Node.js environment. We have to fallback to `setTimeout` for delaying the change detection. PR Close #50820
1 parent 4073cc0 commit 4ba5850

File tree

1 file changed

+24
-4
lines changed

1 file changed

+24
-4
lines changed
 

‎packages/core/src/util/raf.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,35 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8+
89
import {global} from './global';
910

1011
export function getNativeRequestAnimationFrame() {
12+
// Note: the `getNativeRequestAnimationFrame` is used in the `NgZone` class, but we cannot use the
13+
// `inject` function. The `NgZone` instance may be created manually, and thus the injection
14+
// context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
15+
// available because otherwise, we'll fall back to `setTimeout`.
16+
const isBrowser = typeof global['requestAnimationFrame'] === 'function';
17+
18+
// Note: `requestAnimationFrame` is unavailable when the code runs in the Node.js environment. We
19+
// use `setTimeout` because no changes are required other than checking if the current platform is
20+
// the browser. `setTimeout` is a well-established API that is available in both environments.
21+
// `requestAnimationFrame` is used in the browser to coalesce event tasks since event tasks are
22+
// usually executed within the same rendering frame (but this is more implementation details of
23+
// browsers).
1124
let nativeRequestAnimationFrame: (callback: FrameRequestCallback) => number =
12-
global['requestAnimationFrame'];
13-
let nativeCancelAnimationFrame: (handle: number) => void = global['cancelAnimationFrame'];
25+
global[isBrowser ? 'requestAnimationFrame' : 'setTimeout'];
26+
27+
let nativeCancelAnimationFrame: (handle: number) => void =
28+
global[isBrowser ? 'cancelAnimationFrame' : 'clearTimeout'];
29+
1430
if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame! && nativeCancelAnimationFrame!) {
15-
// use unpatched version of requestAnimationFrame(native delegate) if possible
16-
// to avoid another Change detection
31+
// Note: zone.js sets original implementations on patched APIs behind the
32+
// `__zone_symbol__OriginalDelegate` key (see `attachOriginToPatched`). Given the following
33+
// example: `window.requestAnimationFrame.__zone_symbol__OriginalDelegate`; this would return an
34+
// unpatched implementation of the `requestAnimationFrame`, which isn't intercepted by the
35+
// Angular zone. We use the unpatched implementation to avoid another change detection when
36+
// coalescing tasks.
1737
const unpatchedRequestAnimationFrame =
1838
(nativeRequestAnimationFrame as any)[(Zone as any).__symbol__('OriginalDelegate')];
1939
if (unpatchedRequestAnimationFrame) {

0 commit comments

Comments
 (0)
Please sign in to comment.