Skip to content

Commit 1f4ff2f

Browse files
committed
fix(core): avoid triggering on timer and on idle on the server (angular#59177)
This commit updates defer block logic to avoid triggering `on idle` and `on timer` on the server for regular SSR mode (when incremental hydration is not enabled). Triggering the mentioned condition resulted in invoking `setTimeout` calls, which delayed serialization on the server during SSR (the process was waiting for the timeouts to clear). PR Close angular#59177
1 parent 38b090a commit 1f4ff2f

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

packages/core/src/defer/triggering.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,17 @@ export function scheduleDelayedTrigger(
7373
) {
7474
const lView = getLView();
7575
const tNode = getCurrentTNode()!;
76-
const injector = lView[INJECTOR];
77-
const lDetails = getLDeferBlockDetails(lView, tNode);
7876

7977
renderPlaceholder(lView, tNode);
8078

79+
// Exit early to avoid invoking `scheduleFn`, which would
80+
// add `setTimeout` call and potentially delay serialization
81+
// on the server unnecessarily.
82+
if (!shouldTriggerDeferBlock(TriggerType.Regular, lView)) return;
83+
84+
const injector = lView[INJECTOR];
85+
const lDetails = getLDeferBlockDetails(lView, tNode);
86+
8187
const cleanupFn = scheduleFn(
8288
() => triggerDeferBlock(TriggerType.Regular, lView, tNode),
8389
injector,

packages/platform-server/test/full_app_hydration_spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3052,6 +3052,41 @@ describe('platform-server full application hydration integration', () => {
30523052
expect(clientRootNode.outerHTML).toContain('<my-lazy-cmp>Hi!</my-lazy-cmp>');
30533053
});
30543054

3055+
it('should not trigger `setTimeout` calls for `on timer` triggers on the server', async () => {
3056+
const setTimeoutSpy = spyOn(globalThis, 'setTimeout').and.callThrough();
3057+
3058+
@Component({
3059+
selector: 'my-lazy-cmp',
3060+
standalone: true,
3061+
template: 'Hi!',
3062+
})
3063+
class MyLazyCmp {}
3064+
3065+
@Component({
3066+
standalone: true,
3067+
selector: 'app',
3068+
imports: [MyLazyCmp],
3069+
template: `
3070+
@defer (on timer(123ms)) {
3071+
<my-lazy-cmp />
3072+
}
3073+
`,
3074+
})
3075+
class SimpleComponent {}
3076+
3077+
const html = await ssr(SimpleComponent);
3078+
3079+
const ssrContents = getAppContents(html);
3080+
expect(ssrContents).toContain('<app ngh');
3081+
3082+
// Make sure that there were no `setTimeout` calls with the # of ms
3083+
// defined in the `on timer` trigger.
3084+
for (let i = 0; i < setTimeoutSpy.calls.count(); i++) {
3085+
const args = setTimeoutSpy.calls.argsFor(i);
3086+
expect(args[1]).not.toBe(123, 'on timer was triggered during SSR unexpectedly');
3087+
}
3088+
});
3089+
30553090
it('should hydrate a placeholder block', async () => {
30563091
@Component({
30573092
selector: 'my-lazy-cmp',

0 commit comments

Comments
 (0)