Angular Zone guarantees that all async operations (the calls tracked by zone.js ) to be finished before rendering.
Let's take a look at
server.ts
app.get('*', (req, res) => {
res.render('index', { req });
});
||
\/
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
We can see that all regular routes use the Universal engine to render the html.
res.render
method (1) defines default callback.
The ngExpressEngine function returns another function with that callback passed as parameter (2). As soon as that callback is triggered express sends the result to the user.
done = done || function (err, str) {
if (err) return req.next(err);
self.send(str);
};
Now let's see when that callback will be triggered. As mentioned before we need to look at ngExpressEngine function.
getFactory(moduleOrFactory, compiler)
.then(factory => {
return renderModuleFactory(factory, {
extraProviders
});
})
.then((html: string) => {
callback(null, html);
}, (err) => {
callback(err);
});
It will happen only after the promise(3), returning from renderModuleFactory
function, is resolved.
renderModuleFactory
function can be found at @angular/platform-server
export function renderModuleFactory<T>(
moduleFactory: NgModuleFactory<T>,
options: {document?: string, url?: string, extraProviders?: StaticProvider[]}):
Promise<string> {
const platform = _getPlatform(platformServer, options);
return _render(platform, platform.bootstrapModuleFactory(moduleFactory));
}
You can see above that we actually run Angular application here through platform.bootstrapModuleFactory(moduleFactory)
(4)
Inside _render function(5) application waits for bootstrapping to be finished
return moduleRefPromise.then((moduleRef) => {
and after that we can see the key for the answer:
return applicationRef.isStable.pipe((first((isStable: boolean) => isStable)))
.toPromise()
.then(() => {
You can see that angular universal looks at ApplicationRef.isStable observable to know when to finish rendering. In simple words isStable on ApplicationRef is triggered when Zone has no microtasks scheduled (7):
if (!zone.hasPendingMicrotasks) {
try {
zone.runOutsideAngular(() => zone.onStable.emit(null));
