JavaScript 事件循环(宏任务与微任务)

本文详细介绍了JavaScript的执行机制,包括同步任务与异步任务(宏任务和微任务)的处理流程,以及`async`/`await`、`Promise`、`setTimeout`等在事件循环中的行为。通过实例分析了宏任务和微任务的执行顺序,帮助读者理解JS编程中的关键概念。
摘要由CSDN通过智能技术生成

一、知识储备:

js的执行机制

        如下图:

js的事件循环:
  •         执行js代码的时候,遇见同步任务,直接推入调用栈中;
  •         遇到异步任务,将该任务挂起,等到异步任务执行完后推入到任务队列中;
  •         当调用栈中的所有同步任务都执行完,再将任务队列中按顺序一个一个的推入调用栈,重复执行这一操作。

        过程如下图所示:

宏微队列及执行顺序        

宏微队列:

        异步任务又分为宏任务微任务

         宏任务:任务队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列。

        微任务:等宏任务中的主要功能都完成后,渲染引擎不急着去执行下一个宏任务,而是执行当前宏任务中的微任务。

常见的宏任务包含:

  • 执行script标签内部代码、
  • setTimeout/setInterval、
  • ajax请、
  • postMessageMessageChannel、
  • setImmediate,
  • I/O(Node.js)

常见的微任务包含:

  • Promise.then(); Promise.cath()、
  • async/await、
  • process.nextTick(Node.js)、
  • MutonObserver、
  • Object.observe(异步监视对象修改,已废弃)、
  • 加分回答 浏览器和Node 环境下,microtask 任务队列的执行时机不同 - Node端,microtask 在事件循环的各个阶段之间执行 - 浏览器端,microtask 在事件循环的 macrotask 执行完之后执行

注意:1.new Promise()属于同步任务,但是Promise.then(); Promise.cath()属于异步任务的微任务。

           2.async函数里遇到await之前的代码是同步里的,遇到await时,会执行await后面的函数,然后返回一个promise,把await下面的代码放入微任务,并且退出这个async函数。

        3.resolved后的promise对象会在这该级别事件队列结束之后才开始执行,及执行与该轮微任务队列中,始于下一级别宏任务之前

执行顺序:

        1.先执行所有同步任务,遇到异步任务,将任务挂起,等到异步任务有返回之后推入到任务队列;

        2.同步任务执行完毕后,将任务队列中的任务按顺序一个一个的推入并执行;

        3.先执行任务队列里面所有的微任务,如果执行过程中又产生了微任务也会在本次执行过程中执行(即在下一个宏任务执行之前执行)

        4.每次准备取出一个宏任务执行前, 都要将所有的微任务一个一个取出来执行,也就是优先级比宏任务高,且与微任务所处的代码位置无关...依次类推到执行结束。

二、题目汇总:

        做完下面几道题目,js事件循环基本就没有问题了,可以说90%掌握吧

题目一:

解释

async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } console.log('script start') setTimeout(function () { console.log('settimeout') }) async1() new Promise(function (resolve) { console.log('promise1') resolve() }).then(function () { console.log('promise2') }) console.log('script end')

结果如下:
script start
async1 start
async2
promise1
script end
async1 end
promise2
settimeout
详解
考察点 await上面的代码和后面的代码是直接执行的,await下面的代码是要进微任务队列的;new Promise()同步任务;resolve是同步执行的,then里的代码是要进微任务队列的。

执行过程:script start->async1()->async1 start->async2->promise1->script end->微任务处理->async1 end->promise2->宏任务处理->settimeout

题目二:

解释

console.log('1'); // 定义注解 setTimeout_1 用于下文使用方便 setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') }) }) process.nextTick(function() { console.log('6'); }) new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') }) // setTimeout_2 setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') }) }) // 输出结果: 1 7 6 8 2 4 3 5 9 11 10 12

详解:

考察点 new Promise属于同步任务,nextTick放在微任务

执行过程:同步输出1-》同步new Promise 7-》宏任务之前先处理微任务-》6-》8-》第一个宏任务setTimeout-》2-》4-》setTimeout产生的微任务-》3-》5-》第二个setTimeout宏任务-》9-》11-》产生的微任务-》10-》12

题目三:

解释

const promise = new Promise((resolve, reject) => { resolve("10") }).then(res => { console.log("res1:", res) //res1: hahaha return 9 }).then(res => { console.log("res2:", res) //res2: 9 return 8 }).then(res => { console.log("res3:", res) //res3: 8 let promise2=new Promise((resolve,reject)=>{ resolve("p2") }).then(res=>{ console.log(res) setTimeout(function(){ console.log("setTimeout2") },0) }) }) console.log('aaa') setTimeout(function(){ console.log("setTimeout1") },0) const promise1 = new Promise((resolve, reject) => { console.log("p1") resolve(989) }).then(res => { console.log(res) return 990 }).then(res=>{ console.log(res) return 991 }).then(res=>{ console.log(res) return 0 }) /*输出结果: aaa p1 res1: 10 989 res2: 9 990 res3: 8 991 p2 setTimeout1 setTimeout2 */

详解:

考察点 resolved后的promise对象会在这该级别事件队列结束之后才开始执行,及执行与该轮微任务队列中,始于下一级别宏任务之前

执行过程:同步代码aaa-》p1-》宏任务之前把两个微任务处理掉-》res1 10-》989-》处理过程中继续产生微任务,继续处理-》res2 9-》990-》res 8-》991-》处理宏任务之前的微任务p2-》产生了一个宏任务setTimeout放到任务队列里-》执行宏任务,第一个setTimeout-》setTimeout1-》第二个setTimeout-》setTimeout2

题目四:

解释

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"); }); //结果 pr1 2 then1 queueMicrotask1 then3 set1 then2 then4 set2

详解:

考察点 queueMicrotask 来执行微任务,Window 或 Worker 接口的 queueMicrotask() 方法;别的,还需要说?

 执行过程: 

题目还有很多,但是,还需要吗? 

题做错了不要紧,记住下面几句话,再去试试

判断执行顺序大概以下几个重点:

1、promise中的回调函数立刻执行,then中的回调函数会推入微任务队列中,等待调用栈所有任务执行完才执行

2、async函数里遇到await之前的代码是同步里的,遇到await时,会执行await后面的函数,然后返回一个promise,把await下面的代码放入微任务,并且退出这个async函数。

3、调用栈执行完成后,会不断的轮询微任务队列,即使先将宏任务推入队列,也会先执行微任务

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

℡古壹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值