同步异步任务
JS中所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务指的是:
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
console.log 、方法调用就是同步任务
异步任务指的是:
不进入主线程、而进入”任务队列”的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。
宏微任务
异步任务又分为宏任务和微任务
1.宏任务
-
定时器
-
事件绑定
-
ajax
-
回调函数
-
Node中fs可以进行异步的I/O操作
2.微任务
-
Promise(async/await) => Promise并不是完全的同步,在promise中是同步任务,执行resolve或者reject回调的时候,此时是异步操作,会先将then/catch等放到微任务队列。当主栈完成后,才会再去调用resolve/reject方法执行
-
process.nextTick (node中实现的api,把当前任务放到主栈最后执行,当主栈执行完,先执行nextTick,再到等待队列中找)
-
MutationObserver (创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。)
执行顺序优先级:SYNC => MICRO => MACRO
所有JS中的异步编程仅仅是根据某些机制来管控任务的执行顺序,不存在同时执行两个任务这一说法
微任务是可以嵌套的,里面可以在调各种任务
简单考察
看例子:
arcade复制代码setTimeout(()=>{
console.log('setTimeout1')
},0)
let p = new Promise((resolve,reject)=>{
console.log('Promise1')
resolve()
})
p.then(()=>{
console.log('Promise2')
})
最后输出结果是Promise1,Promise2,setTimeout1
Promise参数中的Promise1是同步执行的
其次是因为Promise是微任务,会在同步任务执行完后会去清空microtasks queues,
最后清空完微任务再去宏任务队列取值。
面试常考题目做题方法
同步任务 => 异步任务( 微任务 => 宏任务 )
执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕
宏微任务里面也能嵌套宏微任务
下面是经典题目
setTimeout(_ => console.log(4))
new Promise(resolve => {
resolve()
console.log(1)
}).then(_ => {
console.log(3)
})
console.log(2)
//输出结果为 1、2、3、4
可以看出Promise比setTimeout()先执行。因为Promise定义之后便会立即执行,其后的**.then()是异步里面的微任务**,而setTimeout()是异步的宏任务。异步任务中先执行微任务后执行宏任务。
也就是说, promise 在实例化的过程中所执行的代码都是同步进行的,而 then 中注册的回调才是异步执行的。
在同步代码执行完成后才会去检查是否有异步任务,并执行对应的回调,而微任务又会在宏任务之前执行。
console.log("同步任务1");
function workFun(mac) {
console.log("同步任务2");
if (mac) {
console.log(mac);
}
return new Promise((resolve, reject) => {
console.log("Promise中的同步任务");
resolve("Promise中回调的异步微任务")
})
}
setTimeout(() => {
console.log("异步任务中的宏任务");
setTimeout(() => {
console.log("定时器中的定时器(宏任务)");
}, 0)
workFun("定时器传递任务").then(res => {
console.log('定时器中的:', res);
})
}, 0)
workFun().then(res => {
console.log(res);
})
console.log("同步任务3")
/*
** 同步任务1
同步任务2
Promise中的同步任务
同步任务3
Promise中回调的异步微任务
异步任务中的宏任务
同步任务2
定时器传递任务
Promise中的同步任务
定时器中的: Promise中回调的异步微任务
定时器中的定时器(宏任务)
*/
过一遍执行栈,先执行同步任务
console.log(“同步任务1”);
workFun()
console.log("同步任务2");
if (mac) {
console.log(mac);
}
return new Promise((resolve, reject) => {
console.log("Promise中的同步任务");
resolve("Promise中回调的异步微任务")
})
console.log(“同步任务3”)
排好了不进入主线程的异步任务
宏任务:setTimeout, then(res => { console.log(res); })
微任务:workFun()里嵌套微任务---- resolve("Promise中回调的异步微任务")
先执行所有同步任务,产生结果:
console.log(“Promise中的同步任务”)为什么会先执行,----这是promise的
而resolve(“Promise中回调的异步微任务”)没执行–还在执行同步函数,这是个异步
同步任务1
(执行同步任务workFun函数)
同步任务2
Promise中的同步任务
同步任务3
执行完同步任务,开始异步任务,执行当前所有微任务,产生结果:
Promise中回调的异步微任务
执行排好队的宏任务,也就是
setTimeout
then(res => { console.log(res); })
执行先前排队的setTimeout,依次执行里面的同步任务
console.log("异步任务中的宏任务");
workFun("定时器传递任务")
又排了两个宏任务,
setTimeout(() => { console.log("定时器中的定时器(宏任务)"); },
console.log("异步任务中的宏任务");
此时宏任务:
.then(res => { console.log(‘定时器中的:’, res); })
setTimeout(() => { console.log(“定时器中的定时器(宏任务)”); },
产生结果:
异步任务中的宏任务
同步任务2
定时器传递任务
执行workFun里的同步任务后,执行workFun里的微任务
产生结果:
Promise中的同步任务
继续执行宏任务:
workFun("定时器传递任务").then(res => { console.log('定时器中的:', res); })
-
setTimeout(() => { console.log("定时器中的定时器(宏任务)"); }, 0)
产生结果:.then(res => { console.log(‘定时器中的:’, res); })
定时器中的: Promise中回调的异步微任务
定时器中的定时器(宏任务)
总结
先执行同步和立即执行任务,比如说console.log()、new Promise()
再依次执行微任务,比如说thenable函数和catchable函数
当微任务执行完成后开始执行宏任务,比如说定时器、事件回调等
最后给道es6的,答案留给大家解答讨论
async function method() {
await method2();
console.log(1)
}
function method2() {
const promise = new Promise((resolve) => {setTimeout(() => resolve(), 0)});
return promise;
}
function main() {
method()
console.log(2)
}
main()
参考:
https://blog.csdn.net/weixin_45272449/article/details/122815949
链接:https://juejin.cn/post/6844903638238756878