1. 概念理解
1.1 JavaScript是单线程语言:
单线程: 同一个时间只能做一件事; js是单线程语言, 主要与他的用途有关,作为浏览器的脚本语言, js的主要用途是与用户交互,操作DOM; 这决定了他只能是单线程,否则会带来很多复杂的同步问题;
1.2 同步任务
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务;
1.3 异步任务
不进入主线程,而进入“任务队列”(先进先出的数据结构,排在前面的事件,优先被主线程读取)的任务,自由“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
1.4 事件循环(事件轮询)
指的主线程从'任务队列中'读取事件, 这个过程是循环往复的, 所以整个过程的这种运行机制就叫做 EventLoop(事件循环);
具体过程:
a. 所有的同步任务都在主线程上执行,形成了一个执行栈
b. 主线程之外,还存在一个'任务队列'; 只要异步任务有了运行结果,就在'任务队列'之中放置了一个事件;
c. 一但 '执行栈'中的所有同步任务执行完毕,系统就会读取'任务队列', 将可执行的任务放在主线程执行;任务队列是一个先进先出的数据结构,排在前面的事件,优先被主线程读取;
d. 主线程会不断的重复第三步; 只要主线程空了,就会读取'任务队列';
1.5 宏任务(macro-task) & 微任务(micro-task) 的区分
image.png
1.6 宏任务和微任务之间的关系
image.png
2. 代码示例
- 示例一:
setTimeout(() => {
//执行后 回调一个宏事件
console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');
new Promise((resolve) => {
console.log('外层宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})
分析:
1 浏览器解析js时, 遇到第一个setTimeout , 会分发到宏任务Event queue 中;
2 解析到 "console.log('外层宏事件1');" 时, 会直接执行,输出打印结果;
3 遇到promise 时, new Promise 会立即执行, 输出" console.log('外层宏事件2');", .then事件 会被分发到微任务Event queue中;
4 主线程任务执行完毕后, 开始执行微任务; 即执行两个.then 事件, 并输出 ''微事件1', ''微事件2';
5 微任务执行完毕后, 会执行第二轮宏任务; 即执行setTimeoutm, 输出 '内层宏事件3'
输出结果
外层宏事件1
外层宏事件2
微事件1
微事件2
内层宏事件3
- 示例二:
//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
//微事件1
process.nextTick(function() {
console.log('6');
})
//主线程直接执行
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
//微事件2
console.log('8')
})
//丢到宏事件队列中
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
分析:
1 浏览器解析 主线程遇到第一个console.log('1'); 直接输出结果;
2 解析到setTimeout 为宏任务 , 分发到宏任务Event queue中;
3 解析到 process.nextTick 为微任务, 分发到微任务Event queue中;
4 解析到Promise, new Promise 直接解析执行,输出'7'; '.then()'函数分发到微任务Event queue中;
5 解析到setTimeout直接放到宏任务Event queue中;
6 第一执行完毕后,访问微任务; 输出 '6', '8';
7 微任务执行完毕后, 再次执行宏任务, 第一个setTimeout 输出 '2', nextTick 分发到微任务中, new Promise中的'4' 直接输出; '.then' 分发到微任务中;
8 主线程宏任务执行完毕后, 接着执行微任务, 输出 '3' , '5';
9 接着解析第二个setTimeout 宏任务; "cosnole.log('9')" 直接执行, nextTick 会被分发到微任务中, new promise 的"console.log('11')" 会被直接执行, '.then' 会被放到微任务中;
10 宏任务执行完毕, 访问 微任务, 执行输出 '10', '12'
输出结果
1
7
6
8
2
4
3
5
9
11
10
网友评论