-
Notifications
You must be signed in to change notification settings - Fork 29.5k
Expand file tree
/
Copy pathinject_async.ts
More file actions
145 lines (133 loc) Β· 4.39 KB
/
Copy pathinject_async.ts
File metadata and controls
145 lines (133 loc) Β· 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {IDLE_SERVICE} from '../defer/idle_service';
import {DefaultExport, maybeUnwrapDefaultExport} from '../util/default_export';
import {promiseWithResolvers} from '../util/promise_with_resolvers';
import {assertInInjectionContext} from './contextual';
import {Injector} from './injector';
import {inject} from './injector_compatibility';
import {ProviderToken} from './provider_token';
type InjectAsyncLoaderResult<T> = ProviderToken<T> | DefaultExport<ProviderToken<T>>;
/**
* A helper function that allows to inject dependencies asynchronously,
* which can be useful in cases when the dependency is not needed immediately and can be loaded lazily.
*
* NOTE: To enable lazy loading, the injected service must be auto-provided. This means it should be decorated with either `@Injectable({providedIn: 'root'})` or `@Service()`.
*
* @param loader A function that returns a promise resolving to the injectable service
* @param options Configuration options for the async injection
*
* @returns A function that returns a promise resolving to the requested service instance.
*
* @usageNotes
*
* ```ts
* class MyCmp {
* someSvc = injectAsync(() => import('..'));
*
* async onClick() {
* (await this.someSvc()).handleClick();
* }
* }
*
* // we can also configure prefetching:
* injectAsync(.., {prefetch: onIdle})
* ```
*
* @see [Lazy loading services](guide/di/lazy-loading-services)
* @see [Injection context](guide/di/dependency-injection-context)
*
* @publicApi 22.0
*/
export function injectAsync<T>(
loader: () => Promise<ProviderToken<T>>,
options?: InjectAsyncOptions,
): () => Promise<T>;
export function injectAsync<T>(
loader: () => Promise<DefaultExport<ProviderToken<T>>>,
options?: InjectAsyncOptions,
): () => Promise<T>;
export function injectAsync<T>(
loader: () => Promise<InjectAsyncLoaderResult<T>>,
options?: InjectAsyncOptions,
): () => Promise<T> {
if (ngDevMode) {
assertInInjectionContext(injectAsync);
}
const injector = inject(Injector);
let loadedPromise: Promise<InjectAsyncLoaderResult<T>> | null = null;
const load = () => {
if (!loadedPromise) {
loadedPromise = loader();
}
return loadedPromise;
};
if (options?.prefetch) {
options
.prefetch()
.then(() => load())
.catch(() => {});
}
// We can't use `inject` later on because of the async nature of the loader
return () => load().then((loadedToken) => injector.get(maybeUnwrapDefaultExport(loadedToken))!);
}
/**
* Interface for `options` argument used within `injectAsync` call.
*
* @see [Prefetching the dependency](guide/di/lazy-loading-services#prefetching-the-dependency)
*
* @publicApi 22.0
*/
export interface InjectAsyncOptions {
/**
* A trigger to eagerly prefetch the lazy-loaded dependency before it is requested.
*
*/
prefetch?: PrefetchTrigger;
}
/**
* A function that returns a promise which, when resolved, will trigger the prefetching of
* the lazy-loaded dependency.
*
* @see {@link onIdle}
* @see [Prefetching the dependency](guide/di/lazy-loading-services#prefetching-the-dependency)
*
* @publicApi 22.0
*/
export type PrefetchTrigger = () => Promise<void>;
/**
* A `PrefetchTrigger` helper function to provide the logic of triggering dependency loading
* when the browser becomes idle.
*
* Internally delegates to the configured {@link IdleService}, whose default implementation uses
* [`requestIdleCallback`](https://developer.mozilla.org/docs/Web/API/Window/requestIdleCallback)
* when available and falls back to `setTimeout` otherwise. The default behavior can be replaced
* with `provideIdleServiceWith`.
*
* @usageNotes
*
* ```ts
* injectAsync(import(...), {prefetch: onIdle})
*
* // or with custom idle options:
* injectAsync(import(...), {prefetch: () => onIdle({timeout: 100})})
* ```
*
* @see [Prefetching the dependency](guide/di/lazy-loading-services#prefetching-the-dependency)
*
* @publicApi 22.0
*/
export function onIdle(options?: {timeout?: number}): Promise<void> {
if (ngDevMode) {
assertInInjectionContext(onIdle);
}
const idleService = inject(IDLE_SERVICE);
const {promise, resolve} = promiseWithResolvers<void>();
idleService.requestOnIdle(() => resolve(), options);
return promise;
}