我有以下使用fetch的代码。根据我的理解,在履行承诺之前不会调用回调函数。因此,我期待回调函数在处理其他事物(例如for循环)的过程中执行。但是,它并没有达到我的预期。我的代码如下:
console.log("Before fetch")
fetch('https://example.com/data')
.then(function(response){
console.log("In first then")
return response.json()
})
.then(function(json){
console.log("In second then")
console.log(json)
})
.catch(function(error){
console.log("An error has occured")
console.log(error)
})
console.log("After fetch")
for(let i = 0; i < 1000000; i++){
if (i % 10000 == 0)
console.log(i)
}
console.log("The End")
在履行承诺时,不是立即运行回调,而是在激活回调函数之前等待所有其余代码处理完毕。这是为什么?
我的代码输出如下:
Before fetch
After fetch
0
10000
.
.
.
970000
980000
990000
The End
In first then
In second then
然而,我期待最后两行出现在此之前的某个地方。这里发生了什么,如何更改我的代码,以便反映实际履行承诺的时间?
答案 0 :(得分:1)
这里的关键是你之后运行的for循环是一个长的同步代码块。这就是为什么在JavaScript中不推荐/不建议使用同步API的原因,因为它们阻止所有异步回调执行直到完成。 JavaScript不是多线程的,并且它没有像C中的中断这样的概念,所以如果线程正在执行一个大循环,那么在循环结束之前没有其他任何东西可以运行。
在Node.js中,child_process
API允许您运行守护程序进程,而浏览器的Web Worker API允许并行进程并行运行,这两者都使用基于事件的序列化消息传递来在线程之间进行通信,除此之外,上段中的所有内容都适用于JavaScript。
通常,打破长期同步流程的可能解决方案就是 batching 。使用promises,你可以像这样重写for循环:
(async () => {
for(let i = 0; i < 100000; i++){
if (i % 10000 == 0) {
console.log(i);
// release control for minimum of 4 ms
await new Promise(resolve => { setTimeout(resolve, 0); });
}
}
})().then(() => {
console.log("The End");
});
setTimeout(() => { console.log('Can interrupt loop'); }, 1);
答案 1 :(得分:0)
无论你的承诺有多快实现。回调添加到事件循环,并将在所有同步任务完成后调用。在此示例中,同步任务是for循环。您可以使用setTimeout
尝试使用0ms
的活动,也可以在循环后使用。记住JS是单线程的,并不支持并行任务。
全球化志愿服务青年: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
答案 2 :(得分:-1)
无法保证何时执行回调。 for循环需要很少的处理时间,因此JS引擎可能只是等到它结束才能完成回调函数。除非你把它们作为回调链接起来,否则没有某种方法可以强制执行特定的函数顺序。