答网友问:Await 一个 Promise 对象到底发生了什么

答网友问:Await 一个 Promise 对象到底发生了什么

作者:LanceZhang 2022-09-15 07:54:59

云计算

云原生 让我们看一遍 Node.js 官网对 event-loop 的描述。它强调了一个重点:JS code 是以单线程方式被执行的。

大家好,我是二哥。

前两篇文章发出来后,有一些网友在后台咨询我一些问题,我把它们归总罗列在一起。这篇文章既是答网友问也是对前两篇的补充和复习。

先放下前两篇的链接。

图解 Node.js 的核心 event-loop

​​多图剖析公式 async=Promise+Generator+自动执行器​​

图 1:async 函数代码示例

问 0:上一篇所提到的 generator 和自动执行器是运行在不同的线程里面吗?

答 0:无论是 generator 还是自动执行器,都是在 event-loop 线程也就是运行 JS code 的主线程里面运行的。再强调一遍:它俩不是在两个线程里面运行的。

让我们再看一遍 Node.js 官网对 event-loop 的描述。它强调了一个重点:JS code 是以单线程方式被执行的。

TheeventloopiswhatallowsNode.jstoperformnon-blockingI/OoperationsdespitethefactthatJavaScriptissingle-threadedbyoffloadingoperationstothesystemkernelwheneverpossible.

问 1:await p 这条语句产生了异步请求了吗?

答 1:不,它没有。await 只是在等待 p 状态的改变,无论状态是从 pending 变成 resolved 还是从 pending 变为 rejected 。

问 2:那异步请求是什么时候产生的?

答 2:是在 Promise 的 executor 里面,执行 setTimeout 时产生的。

​下文把 new Promise() 时传递进去的 callback (resolve, reject)=>{ /* your code */} 称为 executor 。其中参数 resolve 和 reject 是由 Promise 自己实现的。需要注意的是这个 executor 是在 new Promise() 的时候,立即执行的。

假如我们在 executor 里面执行的是 fs.read(fd[, options], callback) 这样的语句,那类似地,异步请求是在调用 fs.read() 时产生的。​

问 3:p 状态改变后,为什么通过 resolve(200) 传递的 200 会变成变量 res 的求值结果?

答 3:这就是为什么说我们需要了解 await 背后的实现原理。我们借助图 2 和图 4 来复习一下。

如图 2 所示,async 函数首先转换成了 generator 函数。但 generator 函数自己是不能自动运行的,所以得搭配一个自动执行器,驱动它往前走。自动执行器如同慈爱的妈妈,而 generator 就像那个懵懂的幼儿。小孩子每走一段路都会停下来,回头看看在他身后寸步不离的妈妈,得到妈妈的鼓励或者奖励后,再走向下一个目标。

图 2:async 函数转换成 generator 函数示例

在讲解图 4 之前,还是有必要再次复习两个重要的概念:yield 表达式和 yield 语句。如图 3 所示:

  • a+b 是表达式,它的求值结果影响到的是 { value: xxx, done: xxx } 中的 value 属性,而 { value: xxx, done: xxx } 是调用者通过迭代器调用 next() 方法的返回值 。
  • yield a+b 是 yield 语句,调用者可以通过给 next() 方法传实参来影响 yield 语句的返回值。比如 next(200) 则会使得变量 a1 为 200 。

图 3 还画出了一个重要的地方:generator 函数执行的暂停点:在 yield 表达式求值结束之后,但 yield 语句返回之前。

图 3:yield 表达式和 yield 语句对比

为了更好更清晰地回答问题 3,二哥给大家画了图 4 。

​这一步开始通过执行器调用 generator。

② 虽然对 generator 真正的调用发生在这里,但 generator 函数在 ② 这步其实什么都没有做,只是立即返回了一个迭代器。

③ 自动执行器从这里开始进入驱动 generator 模式。③ 这一步没有给形参 data​ 赋值,因为我们不能在第一次执行  g.next() 的时候给它注入一个值。

④ 这一步每调用一次  g.next() 就会使得 generator 从上次暂停于 yield 的位置开始运行,直到再次遇到 yield 。

⑤ 所以第一次对  g.next() 调用使得左侧 generator 函数从函数起始位置一直运行直到遇到 yield 。

我们看到 ⑤ 所标识出来的代码执行过程其实是创建了一个 Promise 对象,且在 Promise 的 executor 里面设置了一个 1s 钟的定时器。注意,这个 executor 是在创建 Promise 对象时立即执行的,不过 ⑦ 处的代码要等到 1s 之后才会执行。

⑥ generator 函数暂停之前,先会将 yield 表达式的求值结果通过 { value: xxx, done: xxx} 返回给  g.next() 调用方,也即右图 ④ 位置。 

所以你一定猜到了,右图 ④ 位置的变量 result 为 { value: p, done: false} 。这里的 p 就是 ⑤ 执行过程中产生的 Promise 对象。

通过这样的方式,Promise 对象在 generator 函数和自动执行器之间流转。真是一个巧妙的过程。

那么你在右侧 ⑧ 处看到 result.value.then(callback) 这样的语句就不会感到纳闷了,这是 Promise 的标准用法。当 p 的状态变成 resolved 后,⑧ 处的 callback 自然就会得到运行的机会了。

⑦ 1s 很快,滴答一下过去后,resolve(200) 得以运行。它的运行使得 p 的状态变成 resolved,所以在  ⑧ 处耐心等待的 callback 开始了它的工作。

⑧ 是的,这个时候 data 的值为 200 。这是再自然不过的事,如果你对 Promise 的使用了然于胸的话。

⑨ 自动执行器又一次执行 next(data)​ 。不过这一次给它传了一个实参 200 。所以这一次 ④ 处执行的代码变为: g.next(200) 。

⑩ 自动执行器执行  g.next(200) 必然会驱使 generator 函数动身继续往前赶路。

还记得 generator 函数上次停在哪里休息的吗?对,左侧 ⑤ 处箭头所指的位置。generator 函数恢复运行后干的第一件事就是对 yield 语句求值。

如果像   g.next()​  这样驱动它的话,yield 语句返回的是 undefined 。不过这次我们不一样,因为我们执行的是  g.next(200) 。很巧妙,传给 next() 的实参  200 作为 yield 语句的返回值赋值给了左侧变量 res​

图 4:generator + 自动执行器细节图

让我们再回头看下图 1 的示例代码,我们来做个总结:

  1. await p 语句是个糖衣,它包裹的是 yield p 语句 + 自动执行器。
  2. 所谓 await p 暂停并不是说主线程执行  JS  code 暂停了。相反主线程还在继续执行其它的 JS code 。
  3. await 是在等待 p 的状态发生变化。这个等待时间有多长?这完全取决于创建 p 的时候,  executor 里面何时会调用 resolve() 或 reject() 。
  4. 执行 await p 语句的时候,无论 p 的状态是否已经发生了变化,执行到 await p 都会导致 V8 engine 转而去自动执行器里面执行。这是 yield p 语句使然。
  5. 动执行器如同一个如影随形的妈妈,她拿到 p 之后,会耐心地等待,直到得到 p 状态改变后的 value 。最后再通过 g.next(value) 把 value 返回给它挚爱的 generator 函数。

图 5:同图 1

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/250879.html<

(0)
管理的头像管理
上一篇2025-04-28 17:21
下一篇 2025-04-28 17:22

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注