Skip to content

Commit

Permalink
feat(core): support APP_INITIALIZER work with observable
Browse files Browse the repository at this point in the history
add support to observable with APP_INITIALIZER

close angular#15088

Co-authored-by: Andrew Kushnir <43554145+AndrewKushnir@users.noreply.github.com>
  • Loading branch information
vthinkxie and AndrewKushnir committed Feb 10, 2021
1 parent d067dc0 commit 055c508
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 5 deletions.
11 changes: 8 additions & 3 deletions packages/core/src/application_init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {Inject, Injectable, InjectionToken, Optional} from './di';
import {isPromise} from './util/lang';
import {isObservable, isPromise} from './util/lang';
import {noop} from './util/noop';


Expand All @@ -16,8 +16,8 @@ import {noop} from './util/noop';
* one or more initialization functions.
*
* The provided functions are injected at application startup and executed during
* app initialization. If any of these functions returns a Promise, initialization
* does not complete until the Promise is resolved.
* app initialization. If any of these functions returns a Promise or a Observable, initialization
* does not complete until the Promise is resolved or the Observable is completed.
*
* You can, for example, create a factory function that loads language data
* or an external configuration, and provide that function to the `APP_INITIALIZER` token.
Expand Down Expand Up @@ -68,6 +68,11 @@ export class ApplicationInitStatus {
const initResult = this.appInits[i]();
if (isPromise(initResult)) {
asyncInitPromises.push(initResult);
} else if (isObservable(initResult)) {
// Note: `.toPromise()` will be deprecated in RxJS v7 and removed completely in later
// versions. Once RxJS is updated in the repo to v7, this code should be refactored and
// the `lastValueFrom` function should be used instead.
asyncInitPromises.push(initResult.toPromise());
}
}
}
Expand Down
58 changes: 56 additions & 2 deletions packages/core/test/application_init_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
import {Injector} from '@angular/core';
import {APP_INITIALIZER, ApplicationInitStatus} from '@angular/core/src/application_init';
import {Observable, Subscriber} from 'rxjs';

import {inject, TestBed, waitForAsync} from '../testing';

Expand All @@ -28,7 +29,7 @@ import {inject, TestBed, waitForAsync} from '../testing';
})));
});

describe('with async initializers', () => {
describe('with async promise initializers', () => {
let resolve: (result: any) => void;
let promise: Promise<any>;
let completerResolver = false;
Expand Down Expand Up @@ -57,7 +58,7 @@ import {inject, TestBed, waitForAsync} from '../testing';
});
});

it('should update the status once all async initializers are done',
it('should update the status once all async promise initializers are done',
waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
(status as any).runInitializers();

Expand All @@ -73,5 +74,58 @@ import {inject, TestBed, waitForAsync} from '../testing';
});
})));
});

describe('with app initializers represented using observables', () => {
let subscriber: Subscriber<any>;
let observable: Observable<any>;
let completerResolver = false;
beforeEach(() => {
observable = new Observable((res) => {
subscriber = res;
});
TestBed.configureTestingModule({
providers: [
{provide: APP_INITIALIZER, multi: true, useValue: () => observable},
]
});
});

it('should update the status once all async observable initializers are done',
waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
// Accessing internal `runInitializers` function of the `ApplicationInitStatus` class
// instance for testing purposes to invoke initializer functions.
(status as any).runInitializers();

setTimeout(() => {
completerResolver = true;
subscriber.next();
subscriber.complete();
});

expect(status.done).toBe(false);
status.donePromise.then(() => {
expect(status.done).toBe(true);
expect(completerResolver).toBe(true);
});
})));

it('should update the status once async observable initializers has error',
waitForAsync(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
// Accessing internal `runInitializers` function of the `ApplicationInitStatus` class
// instance for testing purposes to invoke initializer functions.
(status as any).runInitializers();

setTimeout(() => {
completerResolver = true;
subscriber.error();
});

expect(status.done).toBe(false);
status.donePromise.catch(() => {
expect(status.done).toBe(false);
expect(completerResolver).toBe(true);
});
})));
});
});
}

0 comments on commit 055c508

Please sign in to comment.