JavaScript 异步之宏队列与微队列
事件循环机制
事件循环的顺序,决定了JavaScript代码的执行顺序
宏任务:setTimeout, setInterval, setImmediate, I/O, UI rendering。
微任务: process.nextTick, Promise, MutationObserver(html5新特性)
浏览器
1.JS线程负责处理JS代码,当遇到一些异步操作的时候,则将这些异步事件移交给Web APIs 处理,自己则继续往下执行。
2.Web APIs线程将接收到的事件按照一定规则添加到任务队列中,宏事件(DOM事件、Ajax事件、setTimeout事件等)添加到宏任务队列中,微事件(Promise、nextTick)添加到微事件队列中。
3.JS线程处理完当前的所有任务以后(执行栈为空),它会先去微任务队列获取事件,并将微任务队列中的所有事件一件件执行完毕,直到微任务队列为空后再去宏任务队列中取出一个事件执行(每次取完一个宏任务队列中的事件执行完毕后,都先检查微任务队列)。
4.然后不断循环第3步。
补充:
- 浏览器标准环境中(比如说谷歌webkit内核),是一个宏任务紧接着所有微任务执行。
- 在node环境中,则又不一样了,是一个类型宏任务队列执行完,再去执行微任务。
宏队列与微队列
异步执行的函数(回调函数)放入队列中执行。队列分为宏队列与微队列。
宏队列:用来保存执行的宏任务(回调),比如:dom事件回调,ajax回调,定时器回调
微队列:用来保存执行的微任务(回调),比如:promise回调,mutation回调
1.JS为单线程引擎,必须先执行所有的初始化同步任务代码。
2.每次取出第一个宏任务执行前,都要将所有的微任务执行完毕。
同步->微队列->宏队列
setTimeout(()=>{ //会立即放入宏队列
console.log("timeout callback1()");
Promise.resolve(3).then(
value =>{ //会立即放入微队列
console.log("Promise onResolved3()",value);
}
)
},0)
setTimeout(()=>{ //会立即放入宏队列
console.log("timeout callback2()");
},0)
Promise.resolve(1).then(
value =>{ //会立即放入微队列
console.log("Promise onResolved1()",value);
}
)
Promise.resolve(2).then(
value =>{ //会立即放入微队列
console.log("Promise onResolved2()",value);
}
)
/*
输出:Promise onResplved(),1
Promise onResplved(),2
timeout callback1()
Promise onResolved3()
timeout callback2()
)
*/
注:取到栈里执行,若有嵌套的情况如下列代码,执行第一个setTimeout回调时,把里面的微任务放入微队列,执行第二宏任务前,先将所有的微任务执行完毕。
promise、async-await、setTimeout的执行顺序
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
1.await是一个让出线程的标志,await后面的函数会先执行一遍,然后跳出整个async函数来执行后面js栈的代码。
2.async函数总是会返回一个Promise的实例,调用一个async函数时,可以理解为里边的代码都是处于new Promise中,所以是同步执行的而最后return的操作,则相当于在Promise中调用resolve。
3.async函数代码执行是同步的,结果返回(相当于then?)是异步的。
4.回来执行await后(相当于.then)执行了
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
})
console.log('script end')
/*
script start
根据点2 console.log('async1 start')
根据点2、点3 console.log('async2') 返回值是promise,await相当于then获取成功的值,要异步!会放入为微队列中等待 await会让出线程,跳出async1继续往下执行
promise1
then是异步的异步的! console.log('promise2')放入微队列等待
script end
async1 end
promise2
setTimeout
*/
await会等同步代码执行完,以及微任务队列清空之后,再执行awati之后的代码。 之后才轮到宏任务队列。