前言:这个真的很重要,开发的时候利用好宏任务和微任务的特性,能够把帮助我们在执行业务函数的时候设定一定的顺序

JavaScript的单线程和任务队列

  1. Js的内部引擎是单线程的,但是浏览器它是多线程的,单线程一意味着所有任务需要排队,前一个任务结束,才会执行后一个任务。
  2. 如果前一个任务耗时很长,后一个任务就不得不一直等待。而且有些时候,有些任务会挂载于等待状态,这个时候其实可以先去运行别的任务,等上一个任务需要再启动的时候,再来运行它。
  3. 于是,为了解决这个问题,有了同步任务(synchronous)和异步任务(asynchronous)。
  4. 同步任务指的是,在主线程上,排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
  5. 异步任务指的是,不进入主线程,而进入“任务队列”(task queue)的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

这里借用一张图,就很好解释了同步和异步任务的执行过程

宏任务和微任务

前面我们已经介绍了同步任务和异步任务的执行过程,为了协调这些任务有条不紊地在主线程上执行,页面进程引入了消息队列和事件循环机制

宏任务(task):就是JS 内部(任务队列里)的任务,严格按照时间顺序压栈和执行。比如 script(整体代码)、 setTimeOut、setInverter、setImmediate 、 MessageChannel等。

微任务(Microtask ):微任务就是一个需要异步执行的函数,执行时机是在主函数执行结束之后、宏任务结束之前。比如process.nextTick、Promise.then()、async/await(实际就是Promise)、MutationObserver(HTML5新特性)

主要的执行顺序是:

运行机制

  1. 在执行栈中执行一个宏任务。
  2. 执行过程中遇到微任务,将微任务添加到微任务队列中
  3. 当前宏任务执行完毕,立即执行微任务队列中的任务。
  4. 当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。
  5. 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。

借用一下别人的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log('---start---');//第一轮主线程

setTimeout(() => {
console.log('setTimeout'); // 将回调代码放入个宏任务队列,第二轮宏任务执行
}, 0);

new Promise((resolve, reject) => {
console.log('---Promise第一轮微任务同步执行---');//第一轮微任务同步执行
resolve()
}).then(()=>{
console.log('Promise.then实例成功回调执行'); // 将回调代码放入微任务队列,第一轮宏任务执行完后立即执行
});

console.log('---end---');//第一轮主线程结束