搞懂js中 eventloop事件循环和Promise面试题

js事件循环中的异步队列有两种:macro(宏任务)队列和 micro(微任务)队列。

常见的 macro-task 比如: setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作、UI 渲染等。

常见的 micro-task 比如: process.nextTick、Promise、MutationObserver 等。

循环过程

我们先看一个完整的 Event Loop 过程。

  1. 初始状态:micro 队列空,macro 队列里有且只有一个 script 脚本(整体代码为宏任务);
  2. 执行script代码,创建的宏任务推到宏任务调用栈中,创建的微任务推到微任务调用栈中;【宏任务阶段】
  3. 执行微任务,调出当前微任务栈的所有微任务,一次执行,其中如果有宏任务推到宏任务栈中【微任务阶段】
  4. 执行宏任务,调出当前宏任务栈中的第一个宏任务,其中创建的微任务推到微任务栈中【宏任务阶段】
  5. 如果代码未结束,循环执行3,4步骤

从上我们可以总结两个规律

  1. 宏任务和微任务阶段是轮番交替进行的
  2. 每个宏任务阶段只执行当前宏任务栈中的第一个任务,执行完就切换到微任务阶段,而微任务阶段却是要执行当前微任务栈的所有微任务,微任务阶段才结束

如果不理解两个规律那么我们就来试着做一道promise的面试题来加深一下

console.log('sync');

setTimeout(function () {
   console.log('setTimeout')
}, 0);

var promise = new Promise(function (resolve, reject) {
   console.log('promise');
   setTimeout(function() {
      promise.then(function(){
        console.log('promise-setTimeout-then')    
      })
      console.log('promise-setTimeout')
   }, 0);
   resolve();
});

promise.then(function() {
    setTimeout(function() { 
            promise.then(function(){
                console.log('then-setTimeout-then')    
            })
            console.log('then-setTimeout')
    }, 0);
    console.log('then');
    promise.then(function(){
        console.log('then-than')    
    })
})

我们先分析一下这段代码中有几个宏任务微任务阶段,我们假装自己是电脑执行一下代码

首先页面加载后同步代码会被推到宏任务中,故第一步是执行script的【宏任务阶段】,我们将宏任务和微任务将分别用两个数组来表示

//宏任务 
macro = ["script同步代码"]
//微任务 
micro = []

script代码开始执行

注://注释部分表示不在当前阶段执行的代码

阶段1【宏任务阶段】

执行第一个宏任务 script同步代码,执行后删除当前宏任务

console.log('sync');
/**监测到settimeout,创建宏任务,并push到宏任务栈中 宏任务+1  macro = ['setTimeout'] **/
//setTimeout(function () {
//    console.log('setTimeout')
//}, 0);

var promise = new Promise(function (resolve, reject) {
   console.log('promise');
   /** 宏任务+1  macro = ['setTimeout','promise-setTimeout'] **/
   setTimeout(function() {
    //   promise.then(function(){
        // console.log('promise-setTimeout-then')    
    //   })
    //   console.log('promise-setTimeout')
   }, 0);
   resolve();
});
/**监测到then,创建宏任务,并push到微任务栈中  微任务+1  micro = ['then']**/
promise.then(function() {
    // setTimeout(function() { 
    //         promise.then(function(){
    //             console.log('then-setTimeout-then')    
    //         })
    //         console.log('then-setTimeout')
    // }, 0);
    // console.log('then');
    // promise.then(function(){
    //     console.log('then-than')    
    // })
})

当同步代码执行后我们任务栈的结果为

//宏任务 
macro = ['setTimeout','promise-setTimeout']
//微任务 
micro = ["then"]

此时同步代码执行可以直接得到一下输出

sync
promise

此时【宏任务阶段】执行完毕,根据上面的规则我们知道,【宏任务阶段】过后就要进入【微任务阶段】了,我们继续往下分析

阶段2【微任务阶段】

// console.log('sync');

// setTimeout(function () {
//    console.log('setTimeout')
// }, 0);

// var promise = new Promise(function (resolve, reject) {
//    console.log('promise');
//    setTimeout(function() {
//       promise.then(function(){
//         console.log('promise-setTimeout-then')    
//       })
//       console.log('promise-setTimeout')
//    }, 0);
//    resolve();
// });

promise.then(function() {
    /** 宏任务+1  macro = ['setTimeout','promise-setTimeout','then-setTimeout'] **/
     setTimeout(function() { 
    //         promise.then(function(){
    //             console.log('then-setTimeout-then')    
    //         })
    //         console.log('then-setTimeout')
     }, 0);
    console.log('then');
    /** 微任务+1  micro = ['then','then-than'] **/
     promise.then(function(){
         console.log('then-than')    
     })
})

根据上面的规则2我们知道,微任务阶段会把微任务栈的任务全部执行,所以此阶段新增的 then-then 也会被执行,最终微任务和宏任务栈里面的结果为

//宏任务 
macro = ['setTimeout','promise-setTimeout','then-setTimeout']
//微任务 
micro = []

所以这个阶段会输出以下结果

then
then-than

根据上面的规则我们知道,【微任务阶段】结束了,又该开始【宏任务阶段】了,我们要注意宏任务阶段只执行任务栈内的第一个宏任务 就是 setTimeout

阶段3【宏任务阶段】

执行宏任务中的第一个任务 setTimeout

// console.log('sync');

setTimeout(function () {
   console.log('setTimeout')
}, 0);

// var promise = new Promise(function (resolve, reject) {
//    console.log('promise');
//    setTimeout(function() {
//       promise.then(function(){
//         console.log('promise-setTimeout-then')    
//       })
//       console.log('promise-setTimeout')
//    }, 0);
//    resolve();
// });

// promise.then(function() {
//     setTimeout(function() { 
//             promise.then(function(){
//                 console.log('then-setTimeout-then')    
//             })
//             console.log('then-setTimeout')
//     }, 0);
//     console.log('then');
//     promise.then(function(){
//         console.log('then-than')    
//     })
// })

第三阶段没有新增任务,执行结果

//宏任务 
macro = ['promise-setTimeout','then-setTimeout']
//微任务 
micro = []

输出

setTimeout

再次执行 微任务

阶段4【微任务阶段】

此时发现微任务栈为空,并没有什么操作,跳过执行第五阶段

阶段5【宏任务阶段】

执行宏任务中第一个任务 promise-setTimeout

// console.log('sync');

// setTimeout(function () {
//    console.log('setTimeout')
// }, 0);

// var promise = new Promise(function (resolve, reject) {
//    console.log('promise');
   setTimeout(function() {
       /** 微任务+1  micro = ['promise-setTimeout-then'] **/
      promise.then(function(){
       // console.log('promise-setTimeout-then')    
      })
      console.log('promise-setTimeout')
   }, 0);
//    resolve();
// });

// promise.then(function() {
//     setTimeout(function() { 
//             promise.then(function(){
//                 console.log('then-setTimeout-then')    
//             })
//             console.log('then-setTimeout')
//     }, 0);
//     console.log('then');
//     promise.then(function(){
//         console.log('then-than')    
//     })
// })

我们看到当前阶段宏任务中又增加了微任务,故执行结果

//宏任务 
macro = ["then-setTimeout"]
//微任务 
micro = ["promise-setTimeout-then"]

输出

promise-setTimeout

继续执行

阶段6【微任务阶段】

执行微任务栈中所有微任务

// console.log('sync');

// setTimeout(function () {
//    console.log('setTimeout')
// }, 0);

// var promise = new Promise(function (resolve, reject) {
//    console.log('promise');
//    setTimeout(function() {
       promise.then(function(){
         console.log('promise-setTimeout-then')    
       })
//       console.log('promise-setTimeout')
//    }, 0);
//    resolve();
// });

// promise.then(function() {
    // setTimeout(function() { 
    //        promise.then(function(){
    //            console.log('then-setTimeout-then')    
    //        })
            // console.log('then-setTimeout')
    // }, 0);
//     console.log('then');
//     promise.then(function(){
//         console.log('then-than')    
//     })
// })

执行结果

//宏任务 
macro = ["then-setTimeout"]
//微任务 
micro = []

输出

promise-setTimeout-then

阶段7【宏任务阶段】

之后阶段,执行最后一个宏任务

// console.log('sync');

// setTimeout(function () {
//    console.log('setTimeout')
// }, 0);

// var promise = new Promise(function (resolve, reject) {
//    console.log('promise');
//    setTimeout(function() {
//       promise.then(function(){
//         console.log('promise-setTimeout-then')    
//       })
//       console.log('promise-setTimeout')
//    }, 0);
//    resolve();
// });

// promise.then(function() {
    setTimeout(function() { 
            /** 微任务+1  micro = ['then-setTimeout-then'] **/
            promise.then(function(){
               // console.log('then-setTimeout-then')    
            })
            console.log('then-setTimeout')
    }, 0);
//     console.log('then');
//     promise.then(function(){
//         console.log('then-than')    
//     })
// })

任务栈

//宏任务 
macro = []
//微任务 
micro = ["then-setTimeout-then"]

输出

then-setTimeout

 

阶段8【微任务阶段】

执行最后一个微任务then-setTimeout-then ,代码省略

输出

then-setTimeout-then

任务栈清空,运行结束

最终版输出结果

//宏任务阶段
sync  
promise
//微任务阶段
then
then - than
//宏任务阶段
setTimeout
//微任务阶段(没有任务,没有输出)
//宏任务阶段
promise - setTimeout
//微任务阶段
promise - setTimeout - then
//宏任务阶段
then - setTimeout
//微任务阶段
then-setTimeout-then

从结果中我们也可以证实上面两个规则。不论多复杂的题目,只要按照这两个规则解析,就可以化繁为简,从容应对

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值