亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

Node.js頂級1%工程師:精通事件循環

在我们努力成为顶级1% Node.js工程师的过程中,了解事件循环非常重要。本文将深入了解事件循环,包括Promise.allPromise.allSettled等概念,以及它们是如何与事件循环交互的。

本文假设读者对v8的事件循环机制有一定了解。在本文的最后一节中,我推荐了两个很棒的资源,适合所有想开始学习或更深入研究这个主题的人。

照片由 Angely Acevedo 拍摄,来自 Unsplash

事件循环入门

在 JavaScript 中,当你调用一个函数时,它会被加入事件队列。V8 引擎会按照加入的顺序来执行这些函数。这构成了 JavaScript 单线程同步执行的基础。这被称为事件循环,因为它会不断检查并执行新的事件。

异步操作和事件循环机制简介

当调用一个异步函数时,执行跳转到该异步函数,并将指向当前代码位置的指针添加到事件队列中。V8 继续执行代码,完成后从事件队列中获取下一个事件。当执行到达该指针时,会跳回原函数继续执行。这种架构让 V8 引擎能够并行运行 JavaScript 代码。

Promise.all 和 Promise.allSettled

(这两个方法在JavaScript中用于处理多个Promise对象的集合。Promise.all 会等待所有Promise完成并返回一个包含结果的数组,而 Promise.allSettled 不管Promise成功或失败都会等待所有Promise完成并返回一个包含每个Promise最终状态的对象数组。)

Promise.allPromise.allSettled 会将作为参数传入的每个Promise同时添加到消息队列中。如果你依次调用异步函数(并等待每个完成),只有第一个会被添加到消息循环中。下一个函数会在前一个执行完毕后才被添加到消息循环。

中断同步操作

有了这些知识,我们可以看到即使在写看似同步的代码时,也有可能跳出同步模式。如果不谨慎处理,这可能会导致意想不到的结果。为了避免这种情况,我建议使用require-await eslint规则(这是一个eslint规则)。

这个例子展示了在 Node.js 中,即使没有任何实际的 I/O 操作,异步处理也可能导致意外的结果。我们来拆解一下:

  1. 我们定义了一个 main 函数,该函数创建名为 data 的对象,其属性 a 设置为 1。
  2. main 使用 Promise.all 同时执行两个函数:fn1(data)fn2(data)。这两个函数都接收同一个名为 data 的对象。
  3. fn1 检查 data.a === 1。最初,这个条件为真。
  4. 在 if 块内部,fn1 调用 await fn0()。这一点很重要,因为它允许其他代码在 fn1 继续执行之前先行运行。
  5. fn1 暂停期间,fn2 执行。它将 data.a 改为 2。
  6. fn1fn0 执行完成后恢复执行。它再次检查 data.a === 1,但现在这个条件为假,因为 fn2 修改了 data
  7. 控制台输出显示这种变化:在调用 await fn0() 之前条件为真,之后则为假。
const fn0 = async () => {  
  console.log("执行fn0");  
};  

const fn1 = async (data) => {  
  console.log("执行fn1");  
  if (data.a === 1) {  
    console.log("条件", data.a === 1);  
    await fn0();  
    console.log("条件不变", data.a === 1);  
    console.log("结束if语句块");  
  }  
};  

const fn2 = (data) => {  
  console.log("执行fn2");  
  data.a = 2;  
};  

async function main() {  
  const data = {  
    a: 1,  
  };  

  await Promise.all([fn1(data), fn2(data)]);  
}  

await main();

从日志的输出可以看出,f2 函数在执行 f1 的时候被执行了。

    正在执行 fn1  
    条件为真  
    正在执行 fn0  
    正在执行 fn2  
    条件为假  
    if 语句执行完毕

简化后的事件列表如下:

  1. 初始状态:当调用 Promise.all 时,fn1fn2 被添加到事件队列中。图中显示它们在各自函数的第 1 行开始执行。
  2. 第一次执行:事件循环首先拾取 fn1 并执行,直到遇到 await fn0() 调用为止。
  3. 回调添加:此时,一个回调被添加到队列中,以便在第 5 行恢复 fn1 的执行(在 await 之后)。同时,fn0 开始执行。
  4. 队列中的下一个:在 fn0 执行完毕后,事件循环接着处理队列中的下一个项目,即执行完 fn2
  5. 最终步骤:队列中唯一剩下的项目是恢复 fn1 的回调,该回调将在第 5 行继续 fn1 的执行。事件循环拾取此回调,fn1 从该点继续执行。

这个例子展示了异步代码如何导致竞态状况。在没有实际异步 I/O 操作的情况下,对象的状态在执行 fn1 的过程中意外地发生了变化。这种行为是因为使用了 async/await 关键字,它们通过与 Node.js 的事件循环交互,提供了并发执行的可能性。

最后的感想

事件循环是一个复杂且充满细节的概念。这篇帖子主要探讨了函数调度的基本概念以及它与 Node.js 中 async/await 的关系,但也总是有更多的东西可以学习。如果你对深入了解感兴趣,我推荐下面这些资源:

  1. Node.js 关于事件循环的文档
  2. Philip Roberts: 事件循环到底是个啥?

我们还看到了如何使用异步函数来控制事件循环的运行,如果不正确理解,不仅强大而且危险的。

在接下来的文章中,我们将深入了解 Node.js 的垃圾收集器,这也是成为顶尖的 Node.js 工程师(仅占 1%)的关键方面。敬请关注!

想了解更多未来的文章,你可以点击这里订阅订阅这里或在我的领英上联系我。

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消