You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

async关键字如何影响函数调用栈?each循环异步执行差异解析

问题解析与async机制说明

首先明确核心差异的原因:Cheerio的.each()是同步遍历方法,而async函数的执行逻辑会拆分同步和异步阶段,这直接导致了两种场景下的报错时机不同。

两种场景的执行顺序拆解

1. 不带async的同步回调

当你传入普通同步函数时,.each()会依次同步执行每个回调:

  • 第一次循环:执行console.log(variableInfo)打印初始值,紧接着执行variableInfo = ...的重赋值操作,立即触发TypeError(const变量不可重赋值),遍历直接终止,因此只会看到一次打印。

2. 带async的异步回调

async函数本质是Promise的语法糖,执行时会拆分为两个阶段:

  • 同步阶段:调用async函数后,会先立即执行函数内的同步代码(比如你的console.log(variableInfo)),直到遇到第一个await(哪怕函数里没有await,async函数也会把后续逻辑包装进微任务)。
  • 异步微任务阶段:同步代码执行完后,函数会暂停并返回一个pending状态的Promise,此时Cheerio的.each()是同步的,不会等待这个Promise完成,直接继续调用下一个循环的async回调,于是所有循环的同步打印都会先完成。
  • 当所有同步代码执行完毕后,事件循环进入微任务队列处理阶段,开始执行每个async函数中暂停的后续代码,第一个执行到variableInfo = ...的操作时,抛出TypeError,此时后续的微任务会被终止,所以你会看到先打印多次初始值,再出现报错。

async函数的实际工作机制

  1. 返回Promise:任何async函数调用后都会返回一个Promise对象,哪怕函数里没有return语句,最终也会返回一个resolved状态的Promise,值为undefined。
  2. 同步优先执行:调用async函数时,会先同步执行函数内的所有代码,直到遇到await关键字或者函数结束:
    • 如果遇到await,会将await后面的表达式转换为Promise,然后暂停函数执行,让出执行权,让调用栈中的其他同步代码先执行。
    • 如果没有await,函数的全部逻辑会被包装成一个resolved的Promise,但同步代码仍然会立即执行,只是后续的错误(比如你的const重赋值)会在微任务阶段抛出。
  3. 错误处理:async函数内的未捕获错误会被自动捕获并作为Promise的reject原因,如果调用者没有通过.catch()或try/catch处理这个reject,错误会在微任务阶段以“未捕获的Promise rejection”形式抛出,这也是为什么你会在所有同步打印之后才看到报错的原因。

内容的提问来源于stack exchange,提问作者merkkupilot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 12:45:39