使用 RxJS timeout 操作符给 Angular SSR 服务器端渲染模式下的 HTTP 请求添加超时机制

Angular Universal 是一个开源项目,扩展了 @angular/platform-server 的功能。 该项目使 Angular 中的服务器端渲染成为可能。

为了在服务器上渲染,Angular 使用 node.js 的 DOM 实现——domino. 对于每个 GET 请求,domino 都会创建一个类似的 Browser Document 对象。 在该对象上下文中,Angular 初始化应用程序,然后向后端发出请求,执行各种异步任务,并将任何来自组件的更改检测应用到 DOM,同时仍在 node.js 环境中运行。渲染引擎随后将 DOM 序列化为字符串并将字符串提供给服务器。服务器将此 HTML 作为对 GET 请求的响应发送。 服务器上的 Angular 应用程序在渲染后被销毁。

使用 Angular Universal 进行服务器端渲染,最常见的一个问题就是,用户在网站上打开一个页面并看到一个白屏。翻译成 Web 应用领域的术语来说,就是首字节时间(Time to First Byte, 简称 TTFB) 过大。TTFB 是指从浏览器请求页面,到它从服务器接收到第一个信息字节之间的时间。在这种情况下,浏览器确实想从服务器接收响应,但请求以超时结束。

在分析这个性能问题之前,我们有必要了解 Zone.js 和 ApplicationRef.

Zone.js 是一个允许开发人员跟踪异步操作的工具。在它的帮助下,Angular 创建了自己的 Zone 并在其中启动应用程序。在 Angular 区域中的每个异步操作结束时,都会触发更改检测。

ApplicationRef 是对正在运行的应用程序(文档)的引用。在这个类的所有功能中,我们对 ApplicationRef.isStable 属性感兴趣。它是一个发出布尔值的 Observable。当 Angular 区域中没有异步任务正在运行时,isStable 为 true,当有任何异步任务时,isStable 为 false.

因此,应用程序的稳定性,取决于 Angular 区域中异步任务的存在与否。

当应用程序的 ApplicationRef.isStable 第一次变为 true 时, Angular 会渲染当前状态的应用程序,并清理平台资源。

我们现在可以假设用户正在尝试打开一个无法达到稳定状态的应用程序。 setInterval、rxjs.interval 或在 Angular 区域中运行的任何其他递归的异步操作,以及 HTTP 请求,都会阻止 Angular 应用进入稳定状态。

我们可以使用 rxjs 的 timeout 操作符,强制使得一个长时间运行的 HTTP 请求超时。

import { timeout, catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';

http.get('<https://example.com>')
  .pipe(
    timeout(2000),
    catchError(e => of(null))
  ).subscribe()

上面例子的逻辑是,如果 HTTP 请求两秒内,没有从服务器端接收到响应,则进入 catchError 错误处理模块内部。

这个解决方案的缺点是,对于每个 HTTP 请求,我们都需要手动为其添加 timeout 操作符。

一种更加优雅的实现是,使用开发包 @ngx-ssr/timeout 里的 NgxSsrTimeoutModule.

如果模块被导入 AppServerModule,那么 HTTP 请求超时将只对服务器端渲染环境起作用。

import { NgModule } from '@angular/core';
import {
  ServerModule,
} from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { NgxSsrTimeoutModule } from '@ngx-ssr/timeout';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    NgxSsrTimeoutModule.forRoot({ timeout: 500 }),
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

Jerry Wang的SAP技术专栏
SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使
868 声望
1.6k 粉丝
0 条评论
推荐阅读
SAP OData 开发实战教程:从入门到提高
本教程根据笔者在 SAP 研究院工作多年的实战经验,计划通过 300 篇以上的文章篇幅,深入介绍 SAP 三大主流的 OData 开发技术:

JerryWang_汪子熙

封面图
从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木142阅读 11.9k评论 10

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木60阅读 6k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.2k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木39阅读 7.1k评论 6

CSS 绘制一只思否猫
欢迎关注我的公众号:前端侦探练习 CSS 有一个比较有趣的方式,就是发挥想象,绘制各式各样的图案,比如来绘制一只思否猫?思否猫,SegmentFault 思否的吉祥物,是一只独一无二、特立独行、热爱自由的(&gt;^ω^&lt...

XboxYan42阅读 2.8k评论 14

封面图
还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan34阅读 2.2k评论 2

封面图
868 声望
1.6k 粉丝
宣传栏