【问题标题】:Extra execution context in async functions异步函数中的额外执行上下文
【发布时间】:2022-01-11 05:39:07
【问题描述】:

当我阅读规范时,我看到了下一个part

  1. 注意:AsyncBlockStart 需要复制执行状态才能恢复其执行。恢复当前状态是不明确的 执行上下文。

我不明白这一点。为什么我们需要复制执行上下文?我们不能在没有额外的执行上下文的情况下做到这一点,或者在这种情况下不复制会破坏什么?

【问题讨论】:

标签: javascript language-lawyer ecmascript-next ecmascript-2017


【解决方案1】:

异步函数体的评估发生在可以重复恢复和暂停的单独执行上下文中。在此上下文中执行的算法步骤在AsyncBlockStart #3 中给出。
await(在Await #8)和完成(即return/throw,在AsyncBlockStart #3.g)上,执行上下文从堆栈中弹出(在await的情况下,暂停以恢复它所在的位置离开了,在Await #9)。 在承诺履行/拒绝(Await #3.c/5.c)和启动异步函数时(AsyncBlockStart #4),它被压入堆栈并恢复。
这些 push/pop 操作需要彼此对称地对应,无论是在启动还是恢复代码时,它都可能会遇到暂停或代码结束;并且在所有四种情况下,堆栈在顶部之前和之后必须具有相同的运行执行上下文。

如果从 Promise 解决中恢复,则正在运行的执行上下文将是当前的 Promise 作业。对于AsyncFunctionStart,正在运行的执行上下文将是在[[Call]] 期间由PrepareForOrdinaryCall 步骤创建并推送到异步函数(通过OrdinaryCallEvaluateBodyEvaluateBody EvaluateAsyncFunctionBody,后者创建承诺并执行 AsyncFunctionStart)。之后它将像任何其他函数一样从[[Call]] #7 的堆栈中弹出。
那么为什么我们需要一个额外的执行上下文呢?因为如果我们不创建一个新的(作为当前的副本),它就会在 AsyncFunctionStart 结束时弹出,并且 [[Call]]将无法再次弹出它。 (或者更糟糕的是,弹出一个太多)。当然,这个问题的另一种解决方案是不复制当前执行上下文,而是重用可挂起的执行上下文,然后再次将其推送到堆栈上(不恢复它,仅将其设置为正在运行的执行context) 在AsyncFunctionStart #4 中的 AsyncBlockStart 之后。但这会很奇怪,不是吗?
毕竟,无论指定哪种方式,结果都是一样的。从用户代码中无法观察到执行上下文。


注意:重复使用相同的执行上下文实际上是生成器所做的。 GeneratorStart #2(从 EvaluateGeneratorBody 调用,参数声明被评估并创建 Generator 实例)确实使用运行执行上下文作为重复恢复和暂停的 genContext。主要区别在于启动(“第一次恢复”)不会在生成器的函数调用期间发生(因为它确实发生在异步函数中),它只会在第一次 next() 调用中发生。

实际上“恢复当前正在执行的上下文是不明确的。”在这里并不适用。当前正在执行的上下文将通过设置“asyncContext 的代码评估状态以便在恢复评估时 […]”在 AsyncBlockStart #3 中隐式挂起,就像在 GeneratorStart #4 中发生的一样.

【讨论】:

  • 很高兴见到你,Bergi :) 你一如既往地好,干得好,我什么都懂。
猜你喜欢
  • 2018-08-20
  • 1970-01-01
  • 1970-01-01
  • 2021-02-18
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
相关资源
最近更新 更多