当前位置: 首页
前端开发
Angular异步执行学习:zone.js用法详解

Angular异步执行学习:zone.js用法详解

热心网友 时间:2026-06-17
转载

什么是 Zone ?

如果你接触过 Angular,大概率听说过 zone.js 这个名字。不过,Angular 为什么要引入它?zone.js 到底能解决什么问题?今天这篇文章,我们先单独把 zone.js 拎出来聊聊。至于它在 Angular 框架里具体扮演什么角色,咱们放到下一篇文章细说。

Angular异步执行学习之zone.js使用

先回答最核心的问题:什么是 Zone?官方文档的定义是:Zone 是一个跨多个异步任务的执行上下文。翻译乘人话就是——Zone 在拦截或追踪异步任务上,有着极其强大的能力。

工作原理剖析

光说不练假把式,我们直接拿一个具体的例子来看看它到底能做些什么,顺便拆解一下背后是怎么实现的。



这个例子很简单:页面加载后,第一个按钮的点击事件会去绑定第二个按钮的事件,第二个按钮的点击则会抛出一个异常。如果我们先后点击“Bind Error”和“Cause Error”这两个按钮,控制台会输出类似这样的错误信息:

(索引):26 Uncaught Error: aw shucks
at HTMLButtonElement.throwError ((索引):26:13)

从报错信息看,只知道第二个按钮的点击函数出了问题,但它是怎么来的、谁绑定的,完全看不到。

zone.js启动

那如果我们用 zone.js 来运行呢?换一下启动代码试试:

Zone.current.fork(
      {
        name: 'error',
        onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) {
          console.log(error.stack);
        }
      }
    ).fork(Zone.longStackTraceZoneSpec).run(main);

这次控制台输出的内容丰富得多:

Error: aw shucks
at HTMLButtonElement.throwError ((索引):26:13)
at ZoneDelegate.invokeTask (zone.js:406:31)
at Zone.runTask (zone.js:178:47)
at ZoneTask.invokeTask [as invoke] (zone.js:487:34)
at invokeTask (zone.js:1600:14)
at HTMLButtonElement.globalZoneAwareCallback (zone.js:1626:17)
at ____________________Elapsed_571_ms__At__Mon_Jan_31_2022_20_09_09_GMT_0800_________ (localhost)
at Object.onScheduleTask (long-stack-trace-zone.js:105:22)
at ZoneDelegate.scheduleTask (zone.js:386:51)
at Zone.scheduleTask (zone.js:221:43)
at Zone.scheduleEventTask (zone.js:247:25)
at HTMLButtonElement.addEventListener (zone.js:1907:35)
at HTMLButtonElement.bindSecondButton ((索引):23:10)
at ZoneDelegate.invokeTask (zone.js:406:31)
at Zone.runTask (zone.js:178:47)
at ____________________Elapsed_2508_ms__At__Mon_Jan_31_2022_20_09_06_GMT_0800_________ (localhost)
at Object.onScheduleTask (long-stack-trace-zone.js:105:22)
at ZoneDelegate.scheduleTask (zone.js:386:51)
at Zone.scheduleTask (zone.js:221:43)
at Zone.scheduleEventTask (zone.js:247:25)
at HTMLButtonElement.addEventListener (zone.js:1907:35)
at main ((索引):20:10)
at ZoneDelegate.invoke (zone.js:372:26)
at Zone.run (zone.js:134:43)

两相对比,差异很明显。没有 zone.js 时,我们只能看到异常是按钮2的点击函数抛出的。而引入 zone.js 之后,不仅能定位到异常本身的抛出处,还能看到这个事件函数是由按钮1的点击函数绑定的,甚至能追溯到整个应用的入口 main 函数。这种跨多个异步任务持续追踪的能力,在大型复杂项目中简直就是救命稻草。那么 zone.js 到底是怎么做到的呢?

秘密在于 zone.js 接管了浏览器提供的原生异步 API,比如点击事件、定时器等。正是通过这种“狸猫换太子”的方式,它获得了对异步操作的更强的控制力。我们拿点击事件来举例,看看它是怎么动手脚的。

proto[ADD_EVENT_LISTENER] = makeAddListener(nativeAddEventListener,..)

这里的 proto 指向的是 EventTarget.prototype,也就是说,这行代码重新定义了 addEventListener 函数。

makeAddListener函数

makeAddListener 内部又做了什么呢?

function makeAddListener() {
  ......
  // 关键代码1
  nativeListener.apply(this, arguments);
  ......
  // 关键代码2
  const task = zone.scheduleEventTask(source, ...)
  ......
}

核心逻辑其实就两件事:第一,在自定义的包装函数里,仍然调用了浏览器原生的 addEventListener;第二,为每个点击函数安排了一个事件任务——也就是执行了 zone.scheduleEventTask。这第二步,才是 Zone 能实现这一切的秘密武器。

回到开头的示例,控制台之所以能输出那么完整的调用栈,靠的正是 onScheduleTask 这个钩子:

onScheduleTask: function (..., task) {
  const currentTask = Zone.currentTask;
  let trace = currentTask && currentTask.data && currentTask.data[creationTrace] || [];
  trace = [new LongStackTrace()].concat(trace);
  task.data[creationTrace] = trace;
}

前面控制台里那串长长的调用栈,实际上就存储在 currentTask.data[creationTrace] 中,它是一个由 LongStackTrace 实例组成的数组。每次有新的异步任务被安排时,onScheduleTask 都会把当前的函数调用栈记录下来。至于这个栈是怎么来的,看看 LongStackTrace 的构造器就明白了:

class LongStackTrace {
    constructor() {
        this.error = getStacktrace();
        this.timestamp = new Date();
    }
}
function getStacktraceWithUncaughtError() {
    return new Error(ERROR_TAG);
}

this.error 保存的正是函数调用栈。看到 new Error 出现,相信大家也大概猜到它获取调用栈的机制了——其实就是利用 Error 对象的栈信息。

这篇文章只是展示了 zone.js 能力的一个侧面。如果你还想深入了解其他功能,建议直接查阅官方文档。但通过这个例子,希望你对 zone.js 已经有了一个直观的认识。毕竟,它是 Angular 变更检测机制中不可或缺的基石。

来源:https://www.jb51.net/javascript/290369swi.htm

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
如何用拖放在Flask看板应用中更新数据库状态

如何用拖放在Flask看板应用中更新数据库状态

在Flask看板应用中,通过HTML5拖放API结合前端fetch异步POST请求,将拖拽任务的task_id和target_state发送至后端路由;后端接收JSON后更新数据库并提交事务,实现跨列拖拽时实时更新状态。需注意将事件绑定到容器而非子元素,并在ondragover中阻止默认行为以确保drop触发。

时间:2026-07-09 07:01
如何用CSS :has()选择器快速实现悬停span显示相邻div

如何用CSS :has()选择器快速实现悬停span显示相邻div

利用CSS的:has()伪类选择器,通过` bar:has(> baz:hover)+ qux`实现悬停内联元素时显示相邻块级容器,解决了传统相邻兄弟选择器无法跨层级匹配的局限,可用于悬停图标显示详情框等交互场景。需注意浏览器兼容性和性能优化,避免过度使用影响渲染效率。

时间:2026-07-09 07:01
display:block居中段落文本的响应式实现方法

display:block居中段落文本的响应式实现方法

通过`display:block`与`max-width`限制宽度,配合`margin:auto`使段落块水平居中并实现响应式布局;同时用CSS统一控制段落间距,避免脱离文档流,提升可读性与维护性。

时间:2026-07-09 07:01
JavaScript代码去重:高效避免重复编写的技巧

JavaScript代码去重:高效避免重复编写的技巧

将重复代码中的变化点提取为参数,利用ES6模板字符串动态生成选择器,可消除重复、保持一致性。注意参数安全性,必要时进行白名单校验;批量化场景可扩展为数组形式;现代框架中更推荐数据驱动或组件化封装替代手动选择器。

时间:2026-07-09 07:00
使用容器查询单位cqw实现表单输入框相对于祖父容器百分比宽度

使用容器查询单位cqw实现表单输入框相对于祖父容器百分比宽度

利用CSS容器查询单位cqw,表单输入框宽度可直接基于祖父容器计算,绕过中间包装器尺寸干扰,实现简洁响应式布局,无需硬编码或JavaScript,显著降低维护成本,提升嵌套结构下的布局灵活性。

时间:2026-07-09 07:00
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜