12-事件循环机制、宏任务和微任务

26,676次阅读
没有评论

共计 3052 个字符,预计需要花费 8 分钟才能阅读完成。

浏览器的事件循环机制(重要)

12- 事件循环机制、宏任务和微任务

执行顺序如下:

  • 同步任务:进入主线程后,立即执行。

  • 异步任务:会先进入 Event Table;等时间到了之后,再进入 任务队列(Event Queue)排队(排队是因为同一时间,JS 只能执行一个任务),先进先出。比如说,setTimeout(()=> {}, 1000)这种定时器任务,需要等一秒之后再进入 Event Queue。

  • 当主线程的任务执行完毕之后,此时主线程处于空闲状态,于是会去读取 Event Queue 中的任务队列,如果有任务,则进入到主线程去执行。

Node.js 事件循环机制

浏览器的 EventLoop 依据的是 HTML5 规范。而 Node.js 的 EventLoop 是由 Node.js 底层的 libuv 规定的。libuv 是一个专注于异步 IO 的跨平台库。

Node.js 的事件循环中,有六个队列。其中,微任务有两个队列,宏任务有四个队列。

一、微任务队列:

  • 顺序 1:next tick queue。比如:process.nextTick
  • 顺序 2:other queue。比如:Promise 的 then 回调、queueMicrotask

二、宏任务队列:

  • 顺序 3:timer queue。比如:setTimeout、setInterval
  • 顺序 4:poll queue。比如:IO 事件
  • 顺序 5:check queue。比如:setImmediate
  • 顺序 6:close queue。比如:close 事件

参考链接:

宏任务和微任务

JS 中的任务分为同步任务、异步任务。

JS 中的异步任务分为宏任务(macrotask)、微任务(microtask)。在早期,异步任务中只有宏任务,没有微任务。后来的语言标准中,推出了“微任务”,因为 希望微任务能够尽早执行

宏任务、微任务分类

事件循环的队列中,有两个队列。

1、宏任务队列,包含这些任务:

  • ajax 网络请求
  • setTimeout、setInterval
  • DOM 事件
  • UI 渲染
  • I/ O 文件读写操作。

2、微任务队列,包含这些任务:

  • Promise 的 then 回调
  • Mutation Observer API:监听 DOM 节点变化。
  • queueMicrotask():可直接将某个任务加入到微任务队列中。

在执行一个 Promise 对象时,当走完 resolve() 进入 fulfilled 状态后,会立刻把 .then()里面的代码加入到 微任务队列 当中。

任务的执行顺序

JS 中的任务执行顺序:同步任务 –> 微任务 –> 宏任务

在执行任何一个宏任务之前(不是队列,是一个宏任务),都会 先查询微任务队列中是否还有任务需要执行

  • 当前宏任务执行之前,必须要保证微任务队列是空的。
  • 如果微任务队列不为空,那就优先执行微任务队列中的任务。

任务执行顺序的面试题

实际开发中,基本不会出现下面这些题目,因为很多时候我们无法精准控制异步任务的执行顺序。但是它们在面试中出现的频率特别高,因为熟悉这些思维训练,有利于考察我们对 JS 单线程、事件循环机制、宏任务和微任务等原理的掌握程度。

题 1:宏任务和微任务的执行顺序

setTimeout(() => {
  // 宏任务
  console.log('setTimeout');
}, 0);

new Promise((resolve, reject) => {resolve();
  console.log('promise1'); // 同步任务
}).then((res) => {
  // 微任务
  console.log('promise then');
});

console.log('同步任务'); // 同步任务

打印结果:

promise1
同步任务
promise then
setTimeout

上方代码执行的顺序依次是:同步任务 –> 微任务 –> 宏任务

题 2:在宏任务中嵌套了微任务

new Promise((resolve, reject) => {setTimeout(() => {resolve();
    console.log('setTimeout'); // 宏任务
  }, 0);
  console.log('promise1');
}).then((res) => {
  // 微任务
  console.log('promise then');
});

console.log('同步任务');

打印结果:

promise1
同步任务
setTimeout
promise then

上方代码解释:在执行宏任务的 过程中 ,创建了一个微任务。但是需要 先把当前这个宏任务执行完 ,再去 创建并执行 微任务。

题 3:综合题

console.log("script start")

setTimeout(() => {console.log("setTimeout1");
  new Promise(resolve => {resolve();
  }).then(() => {
    new Promise(resolve => {resolve();
    }).then(() => {console.log("then1");
    });
    console.log("then2");
  });
});

new Promise(resolve => {
  // 下面这两行代码,即便调换顺序,也不影响打印结果
  console.log("promise1");
  resolve();}).then(() => {console.log("then3");
});

setTimeout(() => {console.log("setTimeout2");
});

console.log('同步代码');

queueMicrotask(() => {console.log("queueMicrotask")
});

new Promise(resolve => {resolve();
}).then(() => {console.log("then4");
});

console.log("script end");

打印结果:

// 第一次循环
script start
promise1
同步代码
script end

// 第二次循环
then3
queueMicrotask
then4

// 第三次循环
setTimeout1
then2
then1

// 第四次循环
setTimeout2

题 4:async await 题目

代码举例:

console.log('script start')

async function async2() {console.log('async2')
}

async function async1() {console.log('async1 start')
  await async2();
  console.log('async1 end')
}

setTimeout(() => {console.log('setTimeout')
}, 0)

async1();

new Promise(resolve => {console.log('promise1')
  resolve();}).then(function () {console.log('then1')
})

console.log('script end');

打印结果:

script start
async1 start
async2
promise1
script end

async1 end
then1

setTimeout

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-09-10发表,共计3052字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)