一、首先我们要了解js的运行机制
1.js是一门单线程语言
单线程是指Js引擎执行Js时只分了一个线程给他执行,也就是执行js时是单线程的,在单线程语言中同一时间只能做一件事情
2.运行环境
js的运行环境一般在node或者浏览器下
3.任务队列
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
二、事件循环机制(Event Loop)

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:
在此次 tick 中选择最先进入队列的任务(异步队列),如果有则执行(一次)
检查是否存在 Microtasks,如果存在则不停地执行,直至清空 Microtasks Queue
更新 render
主线程重复执行上述步骤在上诉tick的基础上需要了解几点:
JS分为同步任务和异步任务
同步任务都在主线程上执行,形成一个执行栈
主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
三、宏任务与微任务
1.宏任务
macrotask,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。
浏览器为了能够使得JS内部macrotask与DOM任务能够有序的执行,会在一个macrotask执行结束后,在下一个macrotask 执行开始前,对页面进行重新渲染,流程如下:
macrotask->渲染->macrotask->...
宏任务包括:script(整体代码)、 setTimeout、 setInterval I/O、 UI交互事件 、postMessage、 MessageChannel、 setImmediate(Node.js 环境)
2.微任务
microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。
所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。
微任务包括: Promise.then 、Object.observe、MutaionObserver、process.nextTick(Node.js 环境)
3.宏任务与微任务的执行顺序

简单的宏任务与微任务执行顺序
console.log('begin')
setTimeout(() => {
//宏任务
console.log('setTimeout')
}, 0);
Promise.resolve().then(() => {
//微任务
console.log('promise');
});
console.log('end');
执行结果是 begin ——> end ——> promise ——> setTimeout
渲染页面与宏任务和微任务的优先级
let con = "<h2>页面渲染</h2>";
document.write(con);
console.log(1);
Promise.resolve().then(()=>{
console.log('promise 2 ');
alert('promise then')
})
setTimeout(()=>{
console.log('setTimeout 3');
alert('setTimeout')
},0)
console.log(4)
执行顺序: 1 ——> 4 ——> promise 2 ——> 页面渲染 ——> setTimeout 3
由此我们可知 微任务 > DOM渲染 > 宏任务