TransferState: кто гарантирует, что данные уже хранятся в состоянии?

Я делаю SSR с TransferState и мне интересно, кто это гарантирует, когда мы делаем

http.get(...).subscribe(data => {
  transferState.set(DATA_KEY, data)
})

данные будут храниться в TransferState? Поскольку http.get является асинхронной операцией, и контент может быть сгенерирован и предоставлен клиенту без этих данных.

1 ответ

Решение

Angular Zone гарантирует, что все асинхронные операции (вызовы, отслеживаемые zone.js) будут завершены до рендеринга.

Давайте посмотрим на

server.ts

app.get('*', (req, res) => {
  res.render('index', { req });
});   
                      ||
                      \/
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

Мы видим, что все обычные маршруты используют универсальный движок для рендеринга html.

res.render Метод (1) определяет обратный вызов по умолчанию. Функция ngExpressEngine возвращает другую функцию с обратным вызовом, переданным в качестве параметра (2). Как только этот обратный вызов срабатывает, экспресс отправляет результат пользователю.

done = done || function (err, str) {
  if (err) return req.next(err);
  self.send(str);
};

Теперь давайте посмотрим, когда этот обратный вызов будет запущен. Как упоминалось ранее, нам нужно взглянуть на функцию ngExpressEngine.

 getFactory(moduleOrFactory, compiler)
   .then(factory => {
       return renderModuleFactory(factory, {
          extraProviders
       });
    })
    .then((html: string) => {
      callback(null, html);
    }, (err) => {
      callback(err);
}); 

Это произойдет только после обещания (3), возвращающегося из renderModuleFactory функция, решена.

renderModuleFactory функцию можно найти на @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));
}

Вы можете видеть выше, что мы на самом деле запускаем приложение Angular здесь через platform.bootstrapModuleFactory(moduleFactory) (4)

Внутри функции _render (5) приложение ожидает завершения загрузки

return moduleRefPromise.then((moduleRef) => {

и после этого мы можем увидеть ключ для ответа:

return applicationRef.isStable.pipe((first((isStable: boolean) => isStable)))
        .toPromise()
        .then(() => {

Вы можете видеть, что angular universal смотрит на наблюдаемый ApplicationRef.isStable, чтобы знать, когда закончить рендеринг. Проще говоря, isStable в ApplicationRef запускается, когда у Zone нет запланированных микрозадач (7):

if (!zone.hasPendingMicrotasks) {
  try {
    zone.runOutsideAngular(() => zone.onStable.emit(null));

Другие вопросы по тегам