JavaScript是单线程的语言,也就是说同一时间只能进行一个任务,毕竟JavaScript可以操作dom,进行ui交互,删除dom和修改dom同时进行,那么应该听哪一个?。
正是因为这样,决定了它是单线程的,但是又说单线程只能在同一个时间内执行一个任务,这样岂不是会造成阻塞?前一个任务执行很久,后面都卡死了,所以就需要JavaScript的事件循环机制来进行分配,这样就会被分为
宏任务:当前调用栈执行的代码称为宏任务宏任务有哪些?
- 主代码块
- 定时器 setTimeout、setInterval、setImmediate
- 同步代码
- I/O
微任务有哪些?
- promise.then
- nextTick
- process
- Object.observe、MutationObserve
- catch finally
console.log('1');
setTimeout(function() {
console.log('2');
}, 0);
Promise.resolve().then(function() {
console.log('3');
}).then(function() {
console.log('4');});
console.log('5');
执行结果:1 5 3 4 2
先了解一下运行机制:
1. 在执行栈中执行一个宏任务(程序刚开始执行时,会把整个代码当成第一个宏任务)。
2. 执行的过程中遇到微任务,先将微任务添加到微任务队列中(它是属于本次执行的宏任务中分出来微任务),遇到宏任务,放到下一个执行栈中。
3. 当前宏任务执行完毕,立即执行任务队列(先进先出的原则)中的微任务
4. 当前微任务队列中的任务执行完毕
5. 接着开始执行执行栈中的下一个宏任务,
6. 依次循环这个规律
解析上面的代码:
1. 整体代码被当成第一个宏任务,代码从上到下执行
2. 遇到console.log('1');是同步任务,直接执行,输出1
3. 遇到setTimeout,是宏任务,放到执行栈中
4. 再往下执行,遇到promise,promise的执行器里是立即执行的,这里没有执行器,而是有then,then是微任务,先放到本次宏任务的微任务队列中,then后面返回的也是一个promise,第二个then也是一个微任务,放到微任务队列中。
5.接着遇到同步代码console.log('5');直接输出5,这时候,第一轮宏任务执行完毕
6.需要去执行本轮它产生的微任务,先进先出的原则,输出3 4
7.这时候循环的第一周期就结束了
8.再去执行栈中的第二个宏任务,输出 2
来个难的练习一下:
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')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
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 7 6 8 2 4 3 5 9 11 10 12
process.nextTick 需要放在node环境下才能执行:
加上async和await
async function async1() {
console.log('1');
let a = await async2();
console.log(a);
console.log('2');
}
async function async2() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('data');
}, 0);
});
}
console.log('3');
setTimeout(function () {
console.log('4');
}, 0);
async1();
new Promise(function (resolve) {
console.log('5');
resolve();
}).then(function () {
console.log('6');
});
console.log('7');
// 3 1 5 7 6 4 data 2
提示:
- await是一个让出线程的标志。
- 正常情况下, await 命令后面是一个 Promise 对象。如果不是,会被转成一个立即 resolve 的 Promise 对象
- 当函数执行的时候, 一旦遇到 await 就会先返回,跳出函数体执行外部的代码,等到异步操作完成,再接着执行函数体内后面的代码。