vlambda博客
学习文章列表

【面试题】JS的EventLoop还不会?(一)



在了解JS消息队列之前,首先要知道不同的异步任务会被分为两类:宏任务微任务

常见的宏任务有:setTimeoutsetIntervalDOM事件AJAX请求

常见的微任务有:Promise.thenasync/awaitprocess.nextTick

大原则:先执行完所有的微任务,再执行宏任务。

当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行

所以执行步骤就是:

  • 从上往下执行主线程的任务(console,new Promise , ........)

  • 从上往下找到微任务,加入到微任务队列

  • 从上往下找到宏任务,加入到宏任务队列

  • 第一次循环结束后,最外层的主线程任务应该全部都已经完成

  • 将微任务队列中的任务挨个执行

    • 此时可能微任务内有微任务,宏任务和主线程,都将其加入到自己对应的队列中

  • 微任务全部完成后,执行宏任务队列

    • 此时可能宏任务内有微任务,宏任务和主线程,都将其加入到自己对应的队列中

  • 第二次循环结束

  • 直接执行主线程,再将微任务队列全部清空后,执行宏任务对列,以此原则进行循环

  • 直到所有队列全部为空,结束这个循环

  • 再执行浏览器空闲状态下会执行的函数

  • 结束

直接做题:

第一题

new Promise(resolve => { console.log('promise'); resolve(5);}).then(value=> console.log('then回调', value)})
function func1() { console.log('func1');}
setTimeout(() => { console.log('setTimeout');});
func1();


解析:

  • 看到new Promise,主线程,输出promise

  • resolve返回一个成功的结果等待then调用,无视不管

  • promise.then,微任务,加入到微任务队列 (微任务队列promise.then)

  • 看到setTimeout(),宏任务,加入到宏任务队列(宏任务队列setTimeout)

  • 执行主线程fun1(),输出 func1

  • 没有主线程了,执行微任务,输出then回调 5

  • 没有微任务了,执行宏任务,输出setTimeout


答案(我在node环境下输出的结果):

[Running] node "c:\Users\asus\Desktop\advert\test.js"promisefunc1then回调 5setTimeout
[Done] exited with code=0 in 0.109 seconds


第二题

在做下面这道题之前需要先知道一个知识点

通过引入 queueMicrotask(),可以避免通过 promise 去创建微任务而带来的风险。举例来说,当使用 promise 创建微任务时,由回调抛出的异常被报告为 rejected promises 而不是标准异常。

不懂也没事,这玩意应该用不上,反正就把这个看成promise.then的函数 ,是个微任务就行


setTimeout(function () { console.log("set1"); new Promise(function (resolve) { resolve();}).then(function () { new Promise(function (resolve) { resolve(); }).then(function () { console.log("then4"); }); console.log("then2");});});
new Promise(function (resolve) { console.log("pr1"); resolve();}).then(function () { console.log("then1");});
setTimeout(function () { console.log("set2");});
console.log(2);
queueMicrotask(() => { console.log("queueMicrotask1")});
new Promise(function (resolve) { resolve();}).then(function () { console.log("then3");});


解析:

因为很难表示队列里面的任务,所以我一般以队列里面即将要输出的标记这个任务

  • 看到setTimeout,宏任务,加入队列(宏任务队列 set 1)

  • 看到new Promise,主线程,直接输出 pr1

    • 将promise.then ,微任务,加入到微任务队列(微任务队列,then1)

  • 看到setTimeout,宏任务,加入队列(宏任务队列 set1 , set2)

  • 看到console.log,主线程,直接输出 2

  • 看到queue,微任务,加入队列(微任务队列,then1 , queue1)

  • 看到new Promise ,主线程,没东西输出

    • 将promise.then ,微任务,加入队列(微任务队列,then1 , queue1,then3)

  • 从头到尾执行了一圈,然后开始执行微任务then1,输出then1

  • 执行微任务queue,输出queueMicrotask1

  • 执行微任务,then3,输出then3

  • 微任务没了,执行宏任务set1

    • 里面首先是console.log,主线程直接输出set1

    • new Promise, 主线程,没东西输出

      • promise.then,微线程,加入队列(微任务队列 then4)

      • console.log,主线程,输出then2

  • 宏任务set1做完了,微任务多了个新的then4,执行,输出then4

  • 执行宏任务set2,输出set2


答案(我在node环境下输出的结果):

[Running] node "c:\Users\asus\Desktop\advert\test.js"pr12then1queueMicrotask1then3set1then2then4set2
[Done] exited with code=0 in 0.158 seconds


还有两个题目,解析写起来太耗时间了,今天晚上就先不写了,明天更。

其中有一题我是在某某阳光上面看到的,答案是错误的,如果有看过那篇文章的伙伴,可以期待明天我给出的正确答案。

希望大家多多点赞,分享,转发【面试题】JS的EventLoop还不会?(一)