Node.js中await fetch对事件循环的影响及行为差异解析
Node.js事件循环:await对同步/异步执行顺序的影响
问题背景
我正在研究Node.js中同步与异步函数的事件循环逻辑,按我的理解同步函数会先执行,之后才轮到异步函数。但在下面的示例中,控制台日志的执行顺序在移除fetchAPI前的await关键字后发生了明显变化,想搞懂背后的原因,我忽略了什么?
带await的代码示例
const experiment = async () =>{ const num = 1 setTimeout(()=>console.log("Hello from timeout"),0) console.log(num) const fetchAPI = await fetch("https://xxxxxxxxxxxx").then(x=>{ console.log("Hello from API") return x.json() }) console.log(fetchAPI.id) console.log("String") Promise.resolve().then(()=>console.log("In promise")) } experiment()
输出结果:
1 Hello from timeout Hello from API 139 String In promise
移除await后的代码示例
移除await后,对应代码行变为:
const fetchAPI = fetch("https://xxxxxxxxxxxx").then(x=>{ console.log("Hello from API") return x.json() })
输出结果:
1 undefined String In promise Hello from timeout Hello from API
核心原因解析
带await时的执行逻辑
await的核心作用是暂停当前async函数的执行,直到后续的Promise状态变为resolved,才会恢复函数执行。- 执行到
await fetch(...)时,experiment函数暂停,线程交还给事件循环。此时事件循环会先处理已进入队列的宏任务:setTimeout的回调属于宏任务,优先执行,输出Hello from timeout。 - 当
fetch请求完成、Promise状态变为resolved后,experiment恢复执行:先触发.then里的console.log("Hello from API"),接着把x.json()的结果赋值给fetchAPI,然后依次执行后续同步代码——输出fetchAPI.id(即返回的随机数139)、String,最后Promise.resolve().then(...)属于微任务,会在当前宏任务结束前执行,输出In promise。
移除await后的执行逻辑
- 去掉
await后,fetch(...)只是创建了一个处于Pending状态的Promise,但不会暂停experiment函数的执行,代码会一路同步跑完:- 先输出
1; - 此时
fetchAPI是一个未完成的Promise对象,打印fetchAPI.id自然得到undefined; - 接着输出
String; Promise.resolve().then(...)作为微任务,会被加入微任务队列,在当前同步代码执行完毕后立即执行,输出In promise。
- 先输出
- 等当前宏任务(
experiment函数的同步执行流程)全部完成后,事件循环才会处理宏任务队列:先执行setTimeout的回调(输出Hello from timeout),之后fetch请求完成,其.then回调作为微任务执行,输出Hello from API。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

