宏任务和微任务
- 宏任务和微任务都是异步任务,都属于一个队列,主要区别于执行顺序
- js机制
遇到宏任务,像执行宏任务,将宏任务放入事件队列中,然后执行微任务,将微任务方式事件队列中,这两个队列不是一个队列,先从微任务中拿回调函数,然后再宏任务中拿宏任务的回调函数。 - 再等待异步任务准备的时候,js引擎先去执行其他任务,等到异步任务准备好了,再去执行回调
- 宏任务队列和微任务队列
- 宏任务:script、setTimeout、setInterval、postMessage、MessageChannel、及Node.js 环境中的setImmediate.
- 微任务: Promise.then、Object.observe、MutationObserver、及Node.js 环境中的process.nextTick
事件循环 eventloop
- 浏览器的事件循环由一个宏任务队列和多个微任务队列组成
- 首先执行第一个宏任务,全局script脚本,产生的宏任务和微任务进入各自的队列,执行完script的代码之后,把当前的微任务队列清空,完成一个事件循环
- 接着在取出一个宏任务,同样把在此期间产生的回调入队,在把当前的微任务队列清空。
- 宏任务队列只有一个,每一个宏任务都有一个自己的微任务队列。每轮循环都是一个宏任务和多个微任务组成的
代码示例
```javascript
setTimeout(function () {
console.log("setTimeout");
new Promise(function (resolve) {
resolve()
}).then(function() {
new Promise(function(resolve) {
resolve()
}).then(function() {
console.log("then4");
})
console.log("then2");
})
})
new Promise(function (resolve) {
console.log("promise1");
resolve()
}).then(function () {
console.log("then1");
})
setTimeout(function() {
console.log("setTimeout2");
})
console.log(2)
queueMicrotask(() => {
console.log("queueMicrotask1");
})
new Promise(function (resolve) {
resolve()
}).then(function () {
console.log("then3");
})
// promise1
// 2
// then1
// queueMicrotask1
// then3
// setTimeout
// then2
// then4
// setTimeout2
```
- 首先在执行代码的时候,
script
为一个宏任务,先将此任务执行,然后进行接下来的代码。遇到setTimeout
将该定时器放入宏任务
队列中。无论是不是零秒。在运行下面的代码。遇到new Promise
将其中的console.log
语句执行。将promise.then()
放入微任务队列中。遇到了setTimeout
将该settimeout
放入到宏任务队列中。遇到了console.log(2)
同步代码,直接输出。然后将queueMicrotask
,这一创建微任务的函数放入微任务队列中,接下来执行new Promise
, 将promise.then
方法放入微任务队列中,这一轮输出了 promise1 和2。接下来清空微任务队列。微任务队列中,有promise.then()
。所以将console.log("then1")
进行输出,然后将queueMicrotask
的函数进行输出,即输出为queueMicrotask1
,接下来清空promise.then
函数,将console.log("then3")
进行输出。这一轮微任务输出了then1, queueMicrotask, then3然后执行宏任务队列中的内容,宏任务队列是一个队列,所以遵循先进先出的原则。宏任务队列中的内容有两个settimeout
。将第一个settimeout先进性输出\将console.log("setTimeout")
进行输出。然后在settimeout中创建了一个new promise ,将这个promise放入微任务队列中。然后输出同步代码console.log("then2")
,这一轮循环结束将微任务进行清空。微任务中只有一个promise.then(),即将then4输出。然后将宏任务队列中的最后一个settimeout执行。即为输出’settimeout2’、console.log("script start");
function requestData(url) {
return new Promise((resolve) => {
setTimeout(() => {
console.log("settimeout");
resolve(url)
}, 2000)
})
}
function getData() {
console.log("getData start")
requestData("why").then(res => {
console.log("then-res", res);
})
console.log("getData end")
}
getData()
console.log("script start");
- 首先代码执行,先输出
script start
,然后执行getData(), 在该函数的代码执行过程中,输出getData start
,requestData().then是异步函数进入微任务队列。然后执行下一行代码,输出getData end
,执行完毕这个函数,然后回到全局作用域中,将script start
进行输出。requestData().then首先要将setTimeout执行完毕才能执行这个微任务,输出同步代码,settimeout,然后执行promise.then方法,输出then-res ,why
console.log("script start");
function requestData(url) {
return new Promise((resolve) => {
setTimeout(() => {
console.log("settimeout");
resolve(url)
}, 2000)
})
}
async function getData() {
console.log("getData start");
const res = await requestData("helloworld")
console.log("then-res1", res);
console.log("getData end");
}
getData()
console.log("script END");
async function async1 () {
console.log("1")
await async2()
console.log("2")
}
async function async2 () {
console.log("3")
}
console.log("4")
setTimeout(function () {
console.log("5")
}, 0)
async1()
new Promise(function (resolve) {
console.log("6")
resolve()
}).then(function () {
console.log("7")
})
console.log("8")
- 代码执行,将async1 和async2 函数提升,输出
console.log(4)
,这一行同步代码,然后执行到async1这个作用域中,然后执行console.log("1")
这个同步代码,遇到await async2()
进入async2()代码块中,然后输出3, 没有显示返回,会包装一个promise.resolve(undefined),所以进入微任务队列中,然后继续下面代码的解析,进入new promise代码块中,输出console.log(6),然后将promise.then放入微任务队列中,执行后面的同步函数console.log(8) ,执行完毕之后,然后执行微任务队列中的语句,将awiat async2执行,然后输出2, 然后执行微任务队列的promise.then,然后将7输出,将这个微任务队列清空之后,然后执行宏任务队列中,settimeout,输出5