网络日志

【刘建】JavaScript宏任务与微任务的原理解析

首先尝试写出这道题的答案

console.log('start')

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

new Promise((resolve) => {
  console.log('promise')
  resolve()
})
  .then(() => {
    console.log('then1')
  })
  .then(() => {
    console.log('then2')
  })

console.log('end')

正确答案如下

start、promise、end、then1、then2、setTimeout

其中涉及到事件循环(event loop),宏任务(macrotask),微任务(microtask);如果完全答对,可以不用看下面分析了。

JavaScript事件循环

同步任务

console.log()
new Promise()

异步任务

// 微任务
Promise后的then()
// 宏任务
setTimeout()

事件循环具体过程:

同步任务进入主线程,异步任务进入Event Table并注册函数。当异步任务完成时,Event Table会将这个函数移入Event Queue。主线程内任务执行完毕执行栈为空时,会去Event Queue读取对应的函数,放入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。

上述名词解释

代码中有两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

JavaScript异步任务

异步任务包含两种:宏任务和微任务。

宏任务:DOM事件回调、AJAX事件回调、定时器回调

微任务:Promise、MutationObserver、node环境中还包括process.nextTick

js运行程序代码是同步的,但执行完所有同步代码后就执行异步代码;异步代码中每次执行宏任务代码前都先执行完微任务的代码后再执行。

宏任务与微任务的执行顺序

执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去检查微任务队列是否为空,如果为空的话,就执行宏任务,否则就一次性执行完所有微任务。 每次单个宏任务执行完毕后,检查微任务队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完微任务后,设置微任务队列为null,然后再执行宏任务,如此循环。

总结:同步—>微任务—>宏任务

一些额外知识

Promise构造函数是同步执行还是异步执行,那么then方法呢?

promise构造函数是同步执行的,then方法是异步执行的

const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})

promise.then(() => {
  console.log(3)
})

console.log(5)

结果1235

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve(2);
  console.log(3);
}).then(val => {
  console.log(val);
});

promise.then(() => {
  console.log(4);
});

console.log(5);

setTimeout(function() {
  console.log(6);
});

结果135246

Promise在new的时候会立即执行里面的代码,then是微任务,会在本次任务执行完的时候执行,setTimeout是宏任务,会在下次任务执行的时候执行。

最后

看更多技术文章,请搜索【刘建全栈技术】