在我预期的时候回调没有被执行

时间:2018-02-13 19:10:05

标签: javascript callback promise

我有以下使用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

然而,我期待最后两行出现在此之前的某个地方。这里发生了什么,如何更改我的代码,以便反映实际履行承诺的时间?

3 个答案:

答案 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);

最少4毫秒的原因:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Reasons_for_delays_longer_than_specified

答案 1 :(得分:0)

无论你的承诺有多快实现。回调添加到事件循环,并将在所有同步任务完成后调用。在此示例中,同步任务是for循环。您可以使用setTimeout尝试使用0ms的活动,也可以在循环后使用。记住JS是单线程的,并不支持并行任务。

全球化志愿服务青年: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

答案 2 :(得分:-1)

无法保证何时执行回调。 for循环需要很少的处理时间,因此JS引擎可能只是等到它结束才能完成回调函数。除非你把它们作为回调链接起来,否则没有某种方法可以强制执行特定的函数顺序。