Skip to content
Closed
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
8 changes: 5 additions & 3 deletions packages/core/src/defer/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,15 +665,16 @@ export function triggerPrefetching(tDetails: TDeferBlockDetails, lView: LView, t
* @param tDetails Static information about this defer block.
* @param lView LView of a host view.
*/
export function triggerResourceLoading(tDetails: TDeferBlockDetails, lView: LView, tNode: TNode) {
export function triggerResourceLoading(
tDetails: TDeferBlockDetails, lView: LView, tNode: TNode): Promise<unknown> {
const injector = lView[INJECTOR]!;
const tView = lView[TVIEW];

if (tDetails.loadingState !== DeferDependenciesLoadingState.NOT_STARTED) {
// If the loading status is different from initial one, it means that
// the loading of dependencies is in progress and there is nothing to do
// in this function. All details can be obtained from the `tDetails` object.
return;
return tDetails.loadingPromise ?? Promise.resolve();
}

const lDetails = getLDeferBlockDetails(lView, tNode);
Expand Down Expand Up @@ -710,7 +711,7 @@ export function triggerResourceLoading(tDetails: TDeferBlockDetails, lView: LVie
tDetails.loadingState = DeferDependenciesLoadingState.COMPLETE;
pendingTasks.remove(taskId);
});
return;
return tDetails.loadingPromise;
}

// Start downloading of defer block dependencies.
Expand Down Expand Up @@ -776,6 +777,7 @@ export function triggerResourceLoading(tDetails: TDeferBlockDetails, lView: LVie
}
}
});
return tDetails.loadingPromise;
}

/** Utility function to render placeholder content (if present) */
Expand Down
38 changes: 37 additions & 1 deletion packages/core/test/defer_fixture_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common';
import {Component, PLATFORM_ID} from '@angular/core';
import {Component, PLATFORM_ID, ɵPendingTasks as PendingTasks} from '@angular/core';
import {DeferBlockBehavior, DeferBlockState, TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';

Expand Down Expand Up @@ -190,6 +190,42 @@ describe('DeferFixture', () => {
expect(el.querySelector('.more')).toBeDefined();
});

it('should not wait forever if application is unstable for a long time', async () => {
@Component({
selector: 'defer-comp',
standalone: true,
imports: [SecondDeferredComp],
template: `
<div>
@defer (on immediate) {
<second-deferred-comp />
}
</div>
`
})
class DeferComp {
constructor(taskService: PendingTasks) {
// Add a task and never remove it. Keeps application unstable forever
taskService.add();
}
}

TestBed.configureTestingModule({
imports: [
DeferComp,
SecondDeferredComp,
],
providers: COMMON_PROVIDERS,
deferBlockBehavior: DeferBlockBehavior.Manual,
});

const componentFixture = TestBed.createComponent(DeferComp);
const deferBlock = (await componentFixture.getDeferBlocks())[0];
const el = componentFixture.nativeElement as HTMLElement;
await deferBlock.render(DeferBlockState.Complete);
expect(el.querySelector('.more')).toBeDefined();
});

it('should work with templates that have local refs', async () => {
@Component({
selector: 'defer-comp',
Expand Down
1 change: 0 additions & 1 deletion packages/core/testing/src/defer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export class DeferBlockFixture {
const skipTimerScheduling = true;
renderDeferBlockState(state, this.block.tNode, this.block.lContainer, skipTimerScheduling);
this.componentFixture.detectChanges();
return this.componentFixture.whenStable();
}

/**
Expand Down