JS事件循环——宏任务和微任务

首先说说结论,对于面试题和日常工作而言,常用的就是对 Promise 和 async/await  的使用了,尤其是面试题,更是会用各种组合来考验你对宏任务微任务的执行顺序。

结论:

  1. 执行完一个宏任务然后会将微任务队列的所有微任务执行完,然后再去 异步定时器队列 去执行里面的定时器任务(即使定时器里有微任务,如then回调和async/await函数等)。
  2. 当Promise有多个then回调时,会把resolve里的第一个then推入微队列,然后继续执行主程序,主程序执行完,再去执行Promise的第一个then,执行完第一个then回调的所有主线程任务和宏任务后会改变Promise状态,然后继续去执行Promise之后的操作,至于在第一个then回调函数中新的微任务和之后then回调,将会添加到微队列中,等到下次读取微任务队列时去调用。可以理解为Promise的所有then回调按需添加到微队列中,这些then回调的微任务中又包含很多宏任务和微任务
  3. 定时器不受async/await 的影响,在 await 等待中有定时器调用的,其定时器同之前规则一致,即等主线程任务执行完再调用定时器队列。
  4. await函数中加入promise的then回调,await会等所有then回调执行完才会继续往下执行,如果在此遇到阻塞,则await后的操作将无法执行。
  5. 在async函数中,await之前的代码是同步执行的,即要等到await函数执行完毕,await之后的相当于then回调,推入下一次的微任务队列里。
  6. 注意,js代码预处理时,会把promise的then回调或其他微任务先添加到微任务队列,按照先进先出的顺序读取微任务队列。

例1:

                                                           

  

例2:

结果:start   end   resolve   timeout

注意:定时器里的操作都是等主线程的任务执行后再执行的(包含在定时器里的then回调函数)

例3:

注意:当Promise有多个then回调时,执行完第一个then回调的所有主线程任务和宏任务后会改变Promise状态,至于在第一个then回调函数中新的微任务和之后then回调,将会添加到微队列中,等到下次读取微任务队列时去调用。如下例4~例7

例4:

在此处,执行输出 “内部第一个then”后,此Promise作为状态已改变,其外部第一个then中的所有主线程任务已全部执行完,执行输出“外部第二个then”,至于“内部第二个then”,会在输出“内部第一个then”后推入微队列。

源代码

new Promise((resolve,reject)=>{
	console.log("外部promise");
	resolve();
}).then(()=>{

	console.log("外部第一个then");

	new Promise((resolve,reject)=>{
		console.log("内部promise");
		resolve(60);
    }).then((data)=>{
		console.log("内部第一个then",data);
    }).then(()=>{
		console.log("内部第二个then");
    });

}).then(()=>{
	console.log("外部第二个then");
})
console.log("最下面");

例5:

例6:

例7:

注意:定时器不受async/await 的影响,在 await 等待中有定时器调用的,其定时器同之前规则一致。

例8:

//结论一
//当执行 then 方法时,如果前面的 promise 已经是 resolved 状态,则直接将回调放入微任务队列中

//结论二
//当一个 promise 被 resolve 时,会遍历之前通过 then 给这个 promise 注册的所有回调,将它们依次放入微任务队列中

//结论三
//对于 then 方法返回的 promise 它是没有 resolve 函数的,取而代之只要 then 中回调的代码执行完毕并获得同步返回值,这个 then 返回的 promise 就算被 resolve

图中步骤是错的,比如步骤2产生的异步宏任务,其实就是直接压进去,不是等到主线程空了才压,顺序应该改一下。

JavaScript处理同步异步任务的方式是:用栈和队列调度分配任务,代码首次运行主线程按照同步任务顺序,依次执行,执行期间产生的异步宏任务则挂起不执行,挂起的方式是添加至宏任务执行队列,产生的微任务也挂起,方式是添加至微任务执行队列。待所有同步任务执行完成,主线程从宏任务执行队列取出最先添加进宏任务执行队列的任务,放入执行栈执行,执行完后立即去执行所有的微任务,微任务清空后再执行下一个宏任务,每执行完一个宏任务就去清空一下微任务,直到两个执行队列全清空。如此循环就是Event Loop事件循环。

对例8的await函数中加入promise的then回调,await会等所有then回调执行完才会继续往下执行,如果在此遇到阻塞,则await后的操作将无法执行。

例9:

但是,async/await 没那么简单,由以下例子得出,在async函数中,await之前的代码是同步执行的,即要等到await函数执行完毕,await之后的相当于then回调,推入下一次的微任务队列里。

例10

难点:

例11:

首先按顺序下来,把promise的then回调放进微任务队列(4),然后执行async1的函数,先输出1,然后执行await的async2函数输出2,在async的await后面的输出3被当成微任务推入微任务队列,此时微任务队列已有promise的then回调,此时async1执行完毕,输出最下面的6,然后主线程的宏任务执行完毕,去读取微任务队列,顺序输出插入的4和3,微任务队列执行完毕,读取定时器队列,输出5,程序执行完毕。

例12:

参考视频:https://www.bilibili.com/video/av59639585?p=19

微任务、宏任务与Event-Loop

Promise 链式调用顺序引发的思考

图解JavaScript事件循环、执行栈、任务队列、宏任务、微任务

发布了142 篇原创文章 · 获赞 54 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/CWH0908/article/details/102892913