一,基本概念:
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务:不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
二,常见的宏任务与微任务:
macrotask:setTimeout,setInterval,setImmediate,requestAnimationFrame,I/O,UI rendering
microtask: process.nextTick,Promises,Object.observe,MutationObserver
Promise是一个构造函数,调用的时候会生成Promise实例。当Promise的状态改变时会调用then函数中定义的回调函数。我们都知道这个回调函数不会立刻执行,他是一个微任务会被添加到当前任务队列中的末尾,在下一轮任务开始执行之前执行。
async/await成对出现,async标记的函数会返回一个Promise对象,可以使用then方法添加回调函数。await后面的语句会同步执行。但 await 下面的语句会被当成微任务添加到当前任务队列的末尾异步执行。
三,遇到事件循环机制问题,运行步骤如下:
* 执行一个宏任务(栈中没有就从事件队列中获取)
* 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
* 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
* 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
* 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
四,程序
【题1】
console.log("我是开始");
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('我是结束');
执行结果:我是开始 我是结束 promise1 promise2 setTimeout
console.log("我是开始"); //主程序代码(宏任务1)
setTimeout(function() { //产生了一个新的宏任务2——————放入宏任务队列
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() { //产生了微任务1——————放入微任务队列
console.log('promise1');
}).then(function() { //产生了微任务2——————放入微任务队列
console.log('promise2');
});
console.log('我是结束'); //主程序代码(宏任务1)
参考:https://yq.aliyun.com/articles/607974
【题2】
let resolvePromise = new Promise(resolve => {
let resolvedPromise = Promise.resolve()
resolve(resolvedPromise)
})
resolvePromise.then(() => {
console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
console.log('promise1')
})
resolvedPromiseThen
.then(() => {
console.log('promise2')
})
.then(() => {
console.log('promise3')
})
结果:
promise1
promise2
resolvePromise resolved
promise3
这道题真的是非常费解了。为什么’resolvePromise resolved’会在第三行才显示呢?
其实这个题目的难点就在于resolve一个Promise对象,js引擎会怎么处理。我们知道Promise.resolve()的参数为Promise对象时,会直接返回这个Promise对象。但当resolve()的参数为Promise对象时,情况会有所不同:
resolve(resolvedPromise)
//等同于:
Promise.resolve().then(() => resolvedPromise.then(resolve, reject));
所以这里第一次执行到这儿的时候() => resolvedPromise.then(resolve, reject)会被放入当前任务列表的最后,然后是Promise1被放入任务列表。没有同步操作了开始执行微任务列表了,这时resolvedPromise是一个已经resolved的Promise直接执行then函数,将resole()函数放入当前队列的最后,输出Promise1。将Promise2放入队列的最后。执行resole(),resolvePromise终于变成了一个resolved状态的Promise对象了,将‘resolvePromise resolved’放入当前任务列表的最后。输出Promise2。将Promise3放到当前任务队列的最后。输出resolvePromise resolved。输出PromiseP.结束!
参考:https://blog.youkuaiyun.com/lncci/article/details/88313328
【题3】
const interval = setInterval(() => {
console.log('setInterval')
}, 0)
setTimeout(() => {
console.log('setTimeout 1')
Promise.resolve().then(() => {
console.log('promise 3')
}).then(() => {
console.log('promise 4')
}).then(() => {
setTimeout(() => {
console.log('setTimeout 2')
Promise.resolve().then(() => {
console.log('promise 5')
}).then(() => {
console.log('promise 6')
}).then(() => {
clearInterval(interval)
})
}, 0)
})
}, 0)
Promise.resolve().then(() => {
console.log('promise 1')
}).then(() => {
console.log('promise 2')
})
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval // 大部分情况下2次, 少数情况下一次
setTimeout 2
promise 5
promise 6
参考:https://segmentfault.com/a/1190000012748907
【题4】
//请写出输出内容
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
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
1. 首先,事件循环从宏任务(macrotask)队列开始,这个时候,宏任务队列中,只有一个script(整体代码)任务;当遇到任务源(task source)时,则会先分发任务到对应的任务队列中去。
2. 然后我们看到首先定义了两个async函数,接着往下看,然后遇到了 `console` 语句,直接输出 `script start`。输出之后,script 任务继续往下执行,遇到 `setTimeout`,其作为一个宏任务源,则会先将其任务分发到对应的队列中。
3. script 任务继续往下执行,执行了async1()函数,前面讲过async函数中在await之前的代码是立即执行的,所以会立即输出`async1 start`。
遇到了await时,会将await后面的表达式执行一遍,所以就紧接着输出`async2`,然后将await后面的代码也就是`console.log('async1 end')`加入到microtask中的Promise队列中,接着跳出async1函数来执行后面的代码。
4. script任务继续往下执行,遇到Promise实例。由于Promise中的函数是立即执行的,而后续的 `.then` 则会被分发到 microtask 的 `Promise` 队列中去。所以会先输出 `promise1`,然后执行 `resolve`,将 `promise2` 分配到对应队列。
5. script任务继续往下执行,最后只有一句输出了 `script end`,至此,全局任务就执行完毕了。
根据上述,每次执行完一个宏任务之后,会去检查是否存在 Microtasks;如果有,则执行 Microtasks 直至清空 Microtask Queue。
因而在script任务执行完毕之后,开始查找清空微任务队列。此时,微任务中, `Promise` 队列有的两个任务`async1 end`和`promise2`,因此按先后顺序输出 `async1 end,promise2`。当所有的 Microtasks 执行完毕之后,表示第一轮的循环就结束了。
6. 第二轮循环依旧从宏任务队列开始。此时宏任务中只有一个 `setTimeout`,取出直接输出即可,至此整个流程结束。
【上题变式一】在第一个变式中将async2中的函数也变成了Promise函数,代码如下:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
//async2做出如下更改:
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise3');
resolve();
}).then(function() {
console.log('promise4');
});
console.log('script end');
/*
script start
async1 start
promise1
promise3
script end
promise2
async1 end
promis4
setTimeout
*/
在第一次macrotask执行完之后,也就是输出`script end`之后,会去清理所有microtask。所以会相继输出`promise2`, ` async1 end`,`promise4`。
【上题变式二】在第二个变式中,将async1中await后面的代码和async2的代码都改为异步的,代码如下:
async function async1() {
console.log('async1 start');
await async2();
//更改如下:
setTimeout(function() {
console.log('setTimeout1')
},0)
}
async function async2() {
//更改如下:
setTimeout(function() {
console.log('setTimeout2')
},0)
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout3');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
/*
script start
async1 start
promise1
script end
promise2
setTimeout3
setTimeout2
setTimeout1
*/
在输出为`promise2`之后,接下来会按照加入setTimeout队列的顺序来依次输出,通过代码我们可以看到加入顺序为`3 2 1`,所以会按3,2,1的顺序来输出。
【上题变式三】
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
/*
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
*/
参考:https://www.cnblogs.com/zyl-Tara/p/10416886.html
【题5】
执行结果:ACBHDEFG