js 的同步任务和异步任务
js代码可以分为两种任务:
-
同步任务(synchronous)—— 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
-
异步任务(asynchronous)—— 不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
异步任务又分为宏任务与微任务:
- 宏任务(macrotask )
事件/函数 浏览器环境 Node.js环境 I/O
✅ ✅ setTimeout
✅ ✅ setInterval
✅ ✅ setImmediate
❌ ✅ requestAnimationFrame
✅ ❌ - 微任务(microtask )
事件/函数 浏览器环境 Node.js环境 process.nextTick
❌ ✅ MutationObserver
✅ ❌ Promise.then catch finally
✅ ✅
js在浏览器中的运行机制
从上到下依次解释执行所有代码
-
遇到同步任务时,在主线程立刻执行该任务。
此时主线程上有一个执行栈(execution context stack),所有同步代码会按顺序执行。
-
遇到异步任务时,异步任务会进入到
Event Table
,当异步任务有结果后,将相对应的回调函数进行注册,放入事件队列(Event Queue);(异步的宏任务有结果后,会放入宏任务事件队列;异步的微任务有结果后,会放入微任务事件队列;)
(1)主线程从上到下依次执行所有同步任务
(2)主线程读取微任务事件队列,若存在微任务,则依次执行所有微任务
- 微任务中同步任务会依次立刻执行
- 微任务中微任务会依次添加到微任务事件队列末尾,待下一次读取微任务事件队列时执行
- 微任务中宏任务会依次添加到宏任务事件队列末尾,待下一次读取宏任务事件队列时执行
微任务执行完毕后,开始DOM 渲染
DOM 渲染结束后,开始事件轮询 Event Loop,执行宏任务
(3)主线程读取宏任务事件队列,若存在宏任务,则依次执行所有宏任务
- 宏任务中同步任务会依次立刻执行
- 宏任务中微任务会依次添加到微任务事件队列末尾,待下一次读取微任务事件队列时执行
- 宏任务中宏任务会依次添加到宏任务事件队列末尾,待下一次读取宏任务事件队列时执行
(4)依次重复第2步和第3步,直到清空微任务事件队列和宏任务事件队列
Event Loop(事件轮询):主线程循环不断从"任务事件队列"中读取事件的运行机制称为Event Loop,这个过程是循环不断的,所以整个的这种运行机制
Event Loop 并不是在 ECMAScript 标准中定义的,而是在 HTML 标准中定义的
测试 js 在浏览器中运行机制的代码
console.log('0号同步任务1');
new Promise(function (resolve) {
console.log('0号同步任务2');
resolve();
}).then(function () {
// 1号微任务
setTimeout(function () {
console.log('1号微任务中的宏任务1');
})
console.log('1号微任务中的同步任务1')
new Promise(function (resolve) {
console.log('1号微任务中的同步任务2');
resolve();
}).then(function () {
// 1.1号微任务
console.log('1.1号微任务中的同步任务1')
setTimeout(function () {
console.log('1.1号微任务中的宏任务1');
})
console.log('1.1号微任务中的同步任务2')
setTimeout(function () {
console.log('1.1号微任务中的宏任务2');
})
})
console.log('1号微任务中的同步任务3')
setTimeout(function () {
console.log('1号微任务中的宏任务2');
})
})
setTimeout(function () {
console.log('0号宏任务中的同步任务1');
new Promise(function (resolve) {
console.log('0号宏任务中的同步任务2');
resolve();
}).then(function () {
// 0号宏任务中的1号微任务
setTimeout(function () {
console.log('0号宏任务中的1号微任务中的宏任务1');
})
console.log('0号宏任务中的1号微任务中的同步任务1')
new Promise(function (resolve) {
console.log('0号宏任务中的1号微任务中的同步任务2');
resolve();
}).then(function () {
// 0号宏任务中的1.1号微任务
console.log('0号宏任务中的1.1号微任务中的同步任务1')
setTimeout(function () {
console.log('0号宏任务中的1.1号微任务中的宏任务1');
})
})
console.log('0号宏任务中的1号微任务中的同步任务3')
})
})
new Promise(function (resolve) {
console.log('0号同步任务3');
resolve();
}).then(function () {
// 2号微任务
setTimeout(function () {
console.log('2号微任务中的宏任务1');
})
console.log('2号微任务中的同步任务1')
new Promise(function (resolve) {
console.log('2号微任务中的同步任务2');
resolve();
}).then(function () {
// 2.1号微任务
console.log('2.1号微任务中的同步任务1')
setTimeout(function () {
console.log('2.1号微任务中的宏任务1');
})
})
console.log('2号微任务中的同步任务3')
})
console.log('0号同步任务4');
运行结果
0号同步任务1
0号同步任务2
0号同步任务3
0号同步任务4
1号微任务中的同步任务1
1号微任务中的同步任务2
1号微任务中的同步任务3
2号微任务中的同步任务1
2号微任务中的同步任务2
2号微任务中的同步任务3
1.1号微任务中的同步任务1
1.1号微任务中的同步任务2
2.1号微任务中的同步任务1
0号宏任务中的同步任务1
0号宏任务中的同步任务2
0号宏任务中的1号微任务中的同步任务1
0号宏任务中的1号微任务中的同步任务2
0号宏任务中的1号微任务中的同步任务3
0号宏任务中的1.1号微任务中的同步任务1
1号微任务中的宏任务1
1号微任务中的宏任务2
2号微任务中的宏任务1
1.1号微任务中的宏任务1
1.1号微任务中的宏任务2
2.1号微任务中的宏任务1
0号宏任务中的1号微任务中的宏任务1
0号宏任务中的1.1号微任务中的宏任务1