JS 同步任务与异步任务理解 2022年5月13日

163 阅读4分钟

参考文章

原文链接:blog.csdn.net/qq_42833001…

JS 运行机制

  • JS是单线程执行
    ”JS是单线程的”指的是JS 引擎线程。
  • 宿主环境
    JS运行的环境。一般为浏览器或者Node。
  • 执行栈
    是一个存储函数调用的栈结构,遵循先进后出的原则。

JS任务分类

  1. 同步任务:会立即执行的任务
  2. 异步任务:不会立即执行的任务(异步任务又分为宏任务与微任务)
  3. 常见的异步任务:Ajax,Dom事件操作,setTimeOut,promise的then方法,Node读取文件
  4. 任务在执行过程中的流程图展示

image.png

在一个任务队列中,可以有多个的宏任务队列,但是微任务队列只有一个. 宏任务与微任务在浏览器和node中的执行有所差异,这个需要特别注意一点 (1)常见的宏任务(macrotask):script, setTimeout, setInterval, setImmeditate, T/O, UI rendering (2)常见的微任务(microtask):process, nextTick, promise.then(), object.observe, MutationObserver 在微任务中,process, nextTick的优先级高于 promise 当一个异步任务进入栈的时候,主线程会判断是同步还是异步任务,如果是同步任务,则立即执行,如果是异步任务,则将该任务交给异步模块进行处理,当异步完事处理到触发条件的时候,根据任务的类型,将回调压入队列之中.如果是宏任务:则新增一个宏任务,任务队列中的宏任务可以有多个,如果是微任务,则直接压入微任务队列(流程如下图所示)

image.png

二.当执行栈为空的时候,宏任务与微任务的执行机制

那么,现在来捋一遍任务的执行机制 (1)从全局的script开始,任务依次入栈,被主线程执行,若执行完则出栈 (2)遇到异步任务的时候,交给异步处理模块进行处理,对应的异步处理模块处理异步需要进行的操作,eg:定时器的计数,异步请求的监听的变更 (3)当异步任务可以执行的时候,事件触发线程将其回调加入任务队列,当栈为空的时候,依次执行. 问题:当异步任务进入任务队列,是宏任务还是微任务? 由于执行栈的入口为script,而全局任务为宏任务,所以当栈为空的时候,同步任务执行完毕,会先执行微任务队列. 微任务执行完毕,会读取宏任务队列中最前的任务 执行宏任务的过程中,如果遇到微任务,则依次加入微任务队列 栈空后,再次读取微任务,依次类推

经典实例

setTimeout(() => {
  console.log("1a");
  Promise.resolve().then(() => {
    console.log("2b");
  });
}, 0);
setTimeout(() => {
  console.log("3c");
}, 0);
Promise.resolve().then(() => {
  console.log("4d");
});
console.log("5a");

代码执行过程如下: 第一轮: 第一个setTimeout 进入宏任务栈, 第二个setTimeout进入宏任务栈, Promise.resolve.then 的任务进入微任务栈 最后的console.log('5a') 直接执行, 第一轮直接输出 5a (第①),

第二轮: 先执行微任务栈中的任务, 输出4d (第②),

第三轮, 开始执行宏任务栈中的第一个setTimeout, 先输出 1a, (第③), 然后 console.log( 2b) 进入微任务栈,

第四轮: 此时, 宏任务栈中有第二个setTimeout, 微任务栈中有 console.log(2b); 所以先执行微任务栈中的任务, 输出 2b (第④), 然后执行宏任务栈的任务, 也就是第二个setTimeout, 输出3c 所以输出顺序为: 5a, 4d, 1a, 2b, 3c

输出结果.png

不怎么经典的第二题

setTimeout(() => {
  console.log("t1");
}, 0);

new Promise((res, rej) => {
  console.log("p");
  res(console.log("res1"));
}).then((result) => {
  console.log(result);
  console.log("result");
});
console.log("end");

解题思路 第一轮:

  1. setTimeout 进入宏任栈,
  2. new Promise 中log('p') 直接输出p, (第①)
  3. res() 中的 log('res1') 直接输出 res1 (第②)
  4. then 中的任务进入微任务中,
  5. 直接输出代码结尾的 end, (第③)

第二轮:

  1. 先执行微任务栈中的任务, log(result), 由于console.log("res1")是没有返回值的, 所以new Promise then中的 result变量为undefined, 所以打印 undefined (第④), 然后打印 result 字符串, 输出 result (第⑤)

  2. 执行宏任务栈中的任务, 输出t1 (第⑥) 所以输出的顺序为, p, res1, end, undefined, result, t1

image.png