js是单线程语言,问题:大家熟知单线程在运行的时候代码都是从上往下执行,那么会有代码堵塞的问题,应该如何解决?
JS在执行的时候会有同步任务和异步任务
同步任务
顺序执行,类似下面代码
console.log("start");
console.log("end");
// 打印结果
start
end
异步任务
就是会把任务放进一个任务队列中,等同步任务执行完再来执行
异步任务有:settimeout, setInterval,ajax ,promise.then()等
注意promise不会进入异步队列,是里面的.then()才是异步任务
console.log("start");
setTimeout(() => {
console.log("timeout");
}, 0);
console.log("end");
// 打印结果
start
end
timeout
但是,如果异步任务的promise.then()和settimeout一起运行的话,顺序应该如何?
// console.log("start");
// Promise.resolve().then(() => {
// console.log("promise");
// });
// setTimeout(() => {
// console.log("timeout");
// }, 0);
// console.log("end");
console.log("start");
setTimeout(() => {
console.log("timeout");
}, 0);
Promise.resolve().then(() => {
console.log("promise");
});
console.log("end");
// 执行结果
script start
script end
promise
timeout
执行之后两次结果为什么是一样的?
是因为异步任务会通过任务队列的机制(先进先出的机制)来进行协调。
任务队列:宏任务(MacroTask)和微任务(MicroTask)
首先js代码会先执行宏任务,如果里面有微任务,就会执行微任务,否则再执行宏任务,这也可以称为事件循环(Event loop)
如图:
宏任务 : scrip(JS 整体代码)、setTimeout、setInterval、setImmediate、I/O、UI 交互
微任务 : Promise(重点关注)、process.nextTick(Node.js)、MutaionObserver
先解释一下上面一段代码
- 首先这段代码是一个宏任务,放进宏任务执行栈中
- 然后输出
start
- 碰到setTimeout,放入宏任务执行栈中
- 碰到Promise.then()放进微任务中
- 输出
end
- 因为整个script是一个宏任务,然后判断任务队列中的微任务,就会输出
promise
- 最后输入第二个宏任务
timeout
总结:
— 最后的最后,记住,JavaScript 是一门单线程语言,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程。
本文参考自一次搞懂-JS事件循环之宏任务和微任务