为何未设setTimeout时循环后变量成伪数组?JS异步机制疑问
为什么必须用setTimeout才能正确访问数组?—— 聊聊JavaScript的异步执行机制
嘿,这问题绝对是JavaScript新手绕不开的坑,我刚学的时候也在这栽过跟头!核心原因就是你对JS的异步执行逻辑还没完全搞明白,咱们一步步拆解:
先搞懂你的代码执行顺序
咱们把你的代码按执行优先级排个序:
- 首先执行同步代码:声明
article_identifiers = []和url = '/articles.json' - 调用
fetch(url):这一步发起了网络请求,但注意——fetch是异步操作!JS不会傻等着请求完成,而是直接把这个请求交给浏览器处理,自己继续往下走 - 如果没加setTimeout,你直接在fetch后面写
console.log(article_identifiers):这行也是同步代码,会立刻执行!这时候fetch的请求还没完成,.then里的循环根本没跑,数组还是空的(你说的“伪数组”应该是误解了空数组的状态,空数组本身是可以用索引访问的,只是没元素而已) - 加了setTimeout之后:
setTimeout的回调会被放进延迟任务队列,要等500ms后才会执行。这时候浏览器有足够的时间完成fetch请求,执行.then里的循环把id塞进数组,所以当console.log跑的时候,数组已经被正确填充了
为什么setTimeout是“碰运气”的写法?
虽然500ms看起来能解决问题,但这其实是不靠谱的:如果网络慢,请求花了600ms才完成,那setTimeout的回调还是会在数组填充前执行,你照样会拿到空数组。
正确的写法:把逻辑放进异步回调里
既然fetch的结果是异步返回的,那所有依赖这个结果的代码,都应该放进.then的回调里,或者用更直观的async/await:
写法1:用.then链式调用
var article_identifiers = []; var url = '/articles.json'; fetch(url) .then((response) => response.json()) .then(data => { var article_amount = Object.keys(data.articles).length; for (var i = 0; i < article_amount; i++) { article_identifiers.push(data.articles[i].id); } // 在这里直接使用数组,因为此时已经填充完成 console.log(article_identifiers); // 所有需要用到这个数组的逻辑都放在这里 })
写法2:用async/await(更易读)
async function getArticleIdentifiers() { let article_identifiers = []; const url = '/articles.json'; // 等待fetch请求完成 const response = await fetch(url); // 等待json解析完成 const data = await response.json(); // 填充数组 const article_amount = Object.keys(data.articles).length; for (let i = 0; i < article_amount; i++) { article_identifiers.push(data.articles[i].id); } // 现在可以放心使用数组了 console.log(article_identifiers); return article_identifiers; } // 调用函数 getArticleIdentifiers();
总结一下
你的核心误解在于:JS是单线程的,异步操作(比如fetch、setTimeout)不会阻塞主线程,它们的回调会被放进任务队列,等主线程的同步代码全部执行完才会被处理。所以直接在fetch外面写console.log,会在数组填充前执行,自然拿不到正确的结果。而setTimeout只是“凑巧”给了异步操作足够的时间完成,但这不是正确的解决方案——把依赖异步结果的代码放进回调里才是正道!
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

