根据关于await的MDN文档:

当在代码中(在异步函数或模块中)遇到等待时,将执行等待的表达式,同时暂停依赖于该表达式的值的所有代码并将其推入微任务队列.

async function foo(name) {
  console.log(name, "start");
  console.log(name, "middle");
  console.log(name, "end");
}

foo("First");
foo("Second");

// First start
// First middle
// First end
// Second start
// Second middle
// Second end

在本例中,这两个异步函数实际上是同步的,因为它们不包含任何等待表达式.这三个陈述在同一时间发生.

这一点很清楚,但有一点:

然而,只要有一个等待,函数就变成异步的,并且后续语句的执行被推迟到下一个节拍.

async function foo(name) {
  console.log(name, "start");
  await console.log(name, "middle");
  console.log(name, "end");
}

foo("First");
foo("Second");

// First start
// First middle
// Second start
// Second middle
// First end
// Second end

我的问题是为什么下一个滴答?这不是事件循环的相同滴答吗?微任务(取决于表达式的值的所有代码)是否将从微任务队列中获取,而不是在下一个刻度中获取?

推荐答案

我的问题是为什么下一个滴答?

Mozilla贡献者对"tick"的定义与NodeJS文档的作者所说的"tick"略有不同,这可能是造成混淆的原因.

这不是事件循环的相同滴答吗?

它确实是在事件循环的相同iteration中执行的.

微任务(取决于表达式的值的所有代码)是否将从微任务队列中获取,而不是在下一个刻度中获取?

then-回调(或其await个函数恢复的对应物)实际上是从微任务队列中获取的.这是否被视为相同或不同的刻度,取决于"刻度"的定义.

References for the differing definitions

NodeJussDocs

这是NodeJS文档定义的event loop个阶段的一个阶段:

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

Each box will be referred to as a "phase" of the event loop.

在同一文档中,此循环的单个迭代被标识为"Tick":

setImmediate()在事件循环的下一次迭代或"滴答"时激发

这意味着在一个节拍(迭代)中,可以执行许多计划的回调,包括微任务队列中的回调.

Mozilla贡献者

Mozilla贡献者以一种更通用、更简单的方式定义了"事件循环"here,但在定义discussing microtasks时,他们所说的内容类似于NodeJS文档:

每个代理都由一个事件循环驱动,该循环收集任何用户和其他事件,将任务排入队列以处理每个回调.然后,它运行所有挂起的JavaScript任务,然后运行所有挂起的微任务,然后执行任何所需的呈现和绘制,然后再次循环以判断挂起的任务.

但这并不完全正确,因为HTML Standard规定事件循环的一次迭代将挑选并执行一个(!)任务,然后将处理任何挂起的微任务.如果有更多任务,则将在事件循环的下一次迭代中处理它们,一次处理一个任务.

主要的区别在于他们所说的"勾号",就像你引用的那样,还有here:

myPromise
  .then((value) => `${value} and bar`)
  .then((value) => `${value} and bar again`)
  .then((value) => `${value} and again`)
  .then((value) => `${value} and again`)
  .then((value) => {
    console.log(value);
  })
  .catch((err) => {
    console.error(err);
  });

Note: For faster execution, all synchronous actions should preferably be done within one handler, otherwise it would take several 100 to execute all handlers in sequence.

这里所说的Tick指的是引擎从微任务队列启动单个(然后)回调,通常是从引擎监控的任何队列调用回调.对于NodeJS文档的作者来说,这些回调都是在同一个"滴答"中进行的.

好像这还不够令人困惑,NodeJS包括一个名为process.nextTick()的函数来调度回调的执行,并且相应的回调队列实际上是在相同的事件循环迭代中处理的!所以名字里有什么……:

在Node.js事件循环的每一轮中,process.nextTick()队列总是在微任务队列之前处理.

结论

你对这个过程的理解是正确的,但是不同的作者对"滴答"这个词的不同定义给这个主题带来了混乱.

我会一起避免使用这个词.

Javascript相关问答推荐

ChartJS:分组堆叠条形图渲染错误

如何计算数组子级的深度- Vue.js

为什么子组件没有在reaction中渲染?

如何比较嵌套对象的更改并创建更改报告

序列查找器功能应用默认值而不是读取响应

RxJS setTimeout操作符等效

可以的.是否可以在不预编译的情况下使用嵌套 Select 器?

使用json文件字符串来enum显示类型字符串无法按照计算的enum成员值的要求分配给类型号

是否有方法在OpenWeatherMap API中获取过go 的降水数据?

Cookie中未保存会话数据

使用下表中所示的值初始化一个二维数组

我应该绑定不影响状态的函数吗?'

如何在Javascript中使用Go和检索本地托管Apache Arrow Flight服务器?

在我的html表单中的用户输入没有被传送到我的google表单中

如何在ASP.NET JavaScript中使用Google Charts API仅对绘制为负方向的条形图移动堆叠条形图标签位置

使用ThreeJ渲染的形状具有抖动/模糊的边缘

WhatsApp Cloud API上载问题:由于MIME类型不正确而导致接收&Quot;INVALID_REQUEST";错误

在forEach循环中获取目标而不是父对象的属性

使用Ace编辑器对子组件实例的native-element 进行Angular 获取时面临的问题

使用jQuery find()获取元素的属性