从一个for循环到任务队列的宏任务和微任务的执行顺序(附大神连接)

从浅入深的解决setTimeout,Promise,Promise.then的先后执行顺序。

如果你已经对for循环,作用域闭包等很熟练了,那么请直接点击**这里,进入到重点**中。

让我们从一个最简单的for循环入手

for (var i = 1;i <= 5;i ++) {
    
    
  console.log(i)
}
//1,2,3,4,5

如果我们修改一下需求,要求每秒输出一次呢?(老生常谈)

for (var i = 1;i <= 5;i ++) {
    
    
  setTimeout(function() {
    
    
    console.log(i)
  },i*1000)
}
//6,6,6,6,6

没错,是每隔一秒输出一次6,一共五次6(不是5呀老哥,虽然之前我也认为是5)
那么,怎么实现每隔一秒输出一次相对应的数字呢?

1、ES 6 let

for(let i = 0;i<5;i++) {
    
    
  setTimeout(function timer(){
    
    
    console.log(i);
  }, i * 1000);
}

2、自调函数

for(let i = 0;i<5;i++) {
    
    
  +function(i){
    
    
    setTimeout(function timer() {
    
    
      console.log(i)
    }, i * 1000);
  }(i);
}

3、setTimeout的扩展参数(第三个参数)

for (var i=1; i<=5; i++) {
    
    
  setTimeout( function timer(i) {
    
    
    console.log(i);    
   }, i*1000,i );
}

然后再来看Promise

new Promise(function (resolve) {
    
    
  console.log('promise1')
  resolve()
 }).then(function () {
    
    
  console.log('then1')
})
//promise1,then1

执行Promise后,打印promise1,然后走了.then函数,打印then1(so easy ————二哈哥)

好,现在稍微复杂一点点(重点)。

new Promise(function (resolve) {
    
    
  console.log('promise1')
  resolve()
 }).then(function () {
    
    
  console.log('then1')
})
for (var i = 1;i <= 5;i ++) {
    
    
  setTimeout(function() {
    
    
    console.log(i)
  },i*1000)
  console.log(i)
}
//promise1,1,2,3,4,5
//then1
//6,6,6,6,6

为什么打印的注释要分三行呢?

因为进行了三次队列任务:

队列一

执行代码–>进入任务队列–>进入Promise函数–>打印promise1–>将Promise.then()加入到Promise.then队列中等待执行–>进入for循环–>将setTimeout加入到setTimeout队列中等待执行–>打印1,2,3,4,5

到此时,已经打印出了promise1,1,2,3,4,5,then1 。现在有两个队列,先选择哪一个呢? 因为for循环中的定时器要在1000ms后执行,所以先执行Promise.then()函数

队列二

执行Promise.then()函数–>输出then1

目前控制台输出promise1,1,2,3,4,5,then1

队列三

一秒后执行一次console.log(i),共执行5次(请注意,此时i已经循环过五次了,此次执行,其数值为6!!!)
打印 6,6,6,6,6

输出完毕,结果为:promise1,1,2,3,4,5,then1,6,6,6,6,6

现在,我们来整理一下,为什么先执行for循环下面的1,2,3,4,5而不是先执行.then函数呢?
因为其涉及到任务队列

任务队列可分为宏任务和微任务。XHR回调、事件回调(鼠标键盘事件)、setImmediate、setTimeout、setInterval、indexedDB数据库操作等I/O都属于宏任务,process.nextTick、Promise.then、MutationObserver(html5新特性)属于微任务。

任务的执行顺序:先按照程序顺序从上往下执行(当前的整体代码我们可以认为是宏任务),遇见宏任务则把他添加到相应的任务队列中,直到第一轮代码执行完;然后再去执行对应的微任务。
例如上述栗子:第一次执行完宏任务输出 promise1,1,2,3,4,5,然后执行微任务输出then1,然后再执行第二轮的宏任务,第二轮的微任务……

累,不知道说清楚了没有,如果有什么问题,一定要及时提出来,哪怕一个错字。

到此栗子基本结束了,但是如果setTimeout不延迟(0ms)或者其内部包含一个new Promise呢?
那么,重点来了

我这边文章是根据大佬的文章简化了,下面附上大佬文章的连接。
大佬直通车?

猜你喜欢

转载自blog.csdn.net/DoLi_JIN/article/details/106126008