JS 事件循环机制(宏任务、微任务)

首先 JavaScript 是单线程的,所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。不妨叫它主线程。
但是实际上还存在其他的线程。例如:处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程(例如在Node.js中)等等。这些线程可能存在于JS引擎之内,也可能存在于JS引擎之外,在此我们不做区分。不妨叫它们工作线程。

JS的执行机制:

  1. 先执行 执行栈中的同步任务
  2. 异步任务 到时间后,会把对应的回调函数放入任务队列中
    任务队列又分:微任务(Microtasks)、宏任务(task)
  • 微任务:先注册的任务先执行(先进先出)。比如:Promise.then DOM改变、process.nextTick
  • 宏任务:比如:整体代码<script>、ajax、定时器(比如:setTimeout)、事件(比如:onclick)、requestAnimationFrame(帧动画)、I/O(文件操作)、UI rendering(样式渲染)
  1. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取 任务队列 中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

这种循环机制叫做:事件循环(event loop)

执行栈(执行栈,执行完当前的所有任务之后会,清空一次微任务,然后去任务队列里读取可执行的宏任务进入主线程,进行执行)
           ↓                                                      ↑
           ↓                           任务循环                    ↑ 按顺序提取一个宏任务进入执行栈
           ↓                                                      ↑
任务队列:微任务(清空之后,会再次访问是否有可执行的宏任务) -------> 宏任务

注意:Promise,自身是同步的,.then 之后的才进入 微任务,process.nextTick 总是比 Promise.then 先执行

多层次代码练习

console.log('1');
setTimeout(function() {
    console.log('2');
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
setTimeout(function() {
    console.log('9');
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
console.log('3');

主线程-微任务-宏任务 练习:(如何使用async、await、Promise以及各种任务的执行特点)

async function fn() {
  console.log(1);
  let b = await fn2();//b拿到return的值 = resolve()里面的值,并阻塞下面的代码。
  console.log(b);
  console.log(2)//出现在await之后,也就是2一定跟着3之后出现
};
function fn2() {
  return new Promise((resolve, reject) => {//async、await要配合Promise使用。
      console.log(9);
      setTimeout(() => {
          resolve(3)//进入宏任务,定时器谁先触发谁先执行。
      }, 1000)
  });
}
// async function fn() {
//     //function fn() {
//     console.log(1);
//     let b = await fn2();
//     console.log(2)//进入微任务,谁先进入谁先触发。
// };
// function fn2() {
//     console.log(9);
//     setTimeout(() => {
//         resolve(3)//进入宏任务,定时器谁先触发谁先执行。
//     }, 1000)
// };

setTimeout(() => {
  console.log(6)//进入宏任务
}, 500);
fn()
let a = new Promise((resolve, reject) => {
  console.log(4);
  resolve()
})
a.then(() => {
  console.log(5)//进入微任务
});
//fn()
console.log(8);
//1,9,4,8,2,5,3,6
//4,1,9,8,5,2,3,6
//注:如果await后面的函数,没有被Promise包着。 await 下面的代码会进微任务队列(谁先进入谁先执行),定时器会进宏任务队列(谁先触发谁先执行)。
//1,9,4,8,5,6,3,2
//await后面的函数被Promise包着,那么await后面的函数才会阻塞下面的函数,等await后面的函数执行完,再往下执行(只在函数体内发生)。

面试题一:

let body = document.body;
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(1);
    });
    console.log(2);
});
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(3);
    });
    console.log(4);
});

面试题二:

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');
}, 0)
async1();
new Promise(function (resolve) {
    console.log('promise1');
    resolve();
}).then(function () {
    console.log('promise2');
});
console.log('script end');

思考?await 下面被阻断的代码,那这一部分代码是如何执行的?

  • await 后面如果是微任务(一个promise),它下面的代码就会等待这个微任务执行完,紧跟着执行。
  • await 后面如果是主任务(比如一个函数或一个表达式),其下面的代码就会等待这一批主任务执行完,最后执行。
posted @ 2021-07-28 19:00  真的想不出来  阅读(469)  评论(0编辑  收藏  举报