关于fetch().then()异步特性的疑问与原理探究
问题解答
长循环为何无法让fetch完成赋值?
JavaScript是单线程模型,所有同步代码会按顺序占据主线程执行,只有当当前执行栈里的所有同步代码全部跑完,才会去处理事件队列里的异步任务(包括fetch的回调)。
你的代码实际执行流程是:
- 调用
loadPeaks()发起fetch请求——这一步仅触发请求,主线程不会等待响应,直接继续执行后续同步代码。 - 打印
myGlobVar without waiting,此时myGlobVar还未被赋值,输出undefined。 - 调用
setTimeout,将其回调函数放入宏任务队列等待执行。 - 开始执行长for循环——这是纯同步代码,会持续阻塞主线程直到循环彻底结束。在循环运行期间,哪怕fetch的响应已经返回,它的
then回调(存放在微任务队列)也完全没有执行机会,因为主线程被循环占满了。 - 循环结束后立刻打印
myGlobVar after for loop——此时执行栈尚未清空(当前正在执行这行console.log代码),微任务队列里的fetch回调还未被处理,因此myGlobVar依然是undefined。 - 只有等这行console.log执行完毕,执行栈彻底清空,JS才会处理微任务队列里的fetch回调,此时
myGlobVar才会被赋值。
简单总结:循环是同步阻塞操作,fetch的回调必须等循环跑完才能执行,所以循环结束后立刻打印时,回调还没来得及给变量赋值。
极大循环数为何导致setTimeout“失效”?
这里的“失效”是两个因素共同作用的结果:
- 主线程被长期阻塞:setTimeout的回调属于宏任务,需要等当前执行栈清空、微任务队列处理完成后才会执行。如果循环执行时间极长(比如10亿次,可能耗时几秒甚至更久),主线程会被持续占用,setTimeout的回调根本没有触发机会,直到循环结束。
- fetch请求超时:浏览器对fetch请求有默认超时限制(通常为几十秒),如果循环运行时间超过这个超时时间,fetch会触发错误进入
catch分支,根本不会执行myGlobVar = peaks这行代码。此时哪怕循环结束,setTimeout回调执行时,myGlobVar依然是undefined。
退一步讲,就算fetch没有超时,循环结束后JS也会优先处理微任务(fetch的回调),再处理宏任务(setTimeout的回调),这时setTimeout里应该能拿到赋值后的变量;但如果循环超时导致fetch请求失败,就会出现undefined的情况。
额外:正确的异步处理姿势
既然是学习原理,顺便提一句,实际开发中绝不会用循环或setTimeout等待异步结果,而是用async/await或者链式then来处理:
async function loadPeaks() { try { const response = await fetch('soundofChengdu.json'); if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return await response.json(); } catch (e) { console.log({ errorIs: e }); } } // 使用示例 loadPeaks().then(peaks => { console.log(peaks); // 直接获取异步结果 // 在这里处理数据逻辑 });
内容的提问来源于stack exchange,提问作者geeheeb
相关产品推荐
相关产品推荐

