宏任务(task)setTimeout、setInterval、Ajax、I/O
、UI交互事件(比如DOM事件)
浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 ?**(task->渲染->task->...)** 鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析HTMl。
微任务(Microtasks )Promise回调、async/await、process.nextTick(
Node独有)MutaionObserver
微任务通常来说,就是需要在当前 task 执行结束后立即执行的任务,比如对一系列动作做出反馈,或或者是需要异步的执行任务而又不需要分配一个新的 task,这样便可以减小一点性能的开销。
?只要执行栈中没有其他的js代码正在执行且每个宏任务执行完,微任务队列会立即执行。如果在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。微任务包括了mutation 、mutation 的回调还有接下来的例子promise的回调。
来看一段代码
async function test1() {
console.log('start test1');
console.log(await test2());
console.log('end test1');
}
async function test2() {
console.log('test2');
return 'return test2 value'
}
test1();
console.log('start async');
setTimeout(() => {
console.log('setTimeout');
}, 0);
new Promise((resolve, reject) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
});
模拟执行上面的代码
1)执行 test1 函数,输出‘statr test1’;
2)执行 console.log(await test2());根据debug顺序来看,是先执行test2() 它是一个async函数,输出 'test2' ;因为函数 retrun 有值 ,因此返回的Promise对象也是有值的,该Promise是下面这样的 ;但是因为有 await 的缘故 随后 console.log("return test2 value") 进入 微任务队列
3)await 之后的代码也 console.log('end test1') 也进入微任务队列中
4)执行console.log('start async'); 输出 'start async'
5)遇到 setTimeout(...) 进入宏任务队列
6)执行new Promise (..); 执行console.log('promise1') 输出 'promise1' ;
执行resolve(); 改变该Promise状态;
.then()中的代码进入微任务队列
7)现在调用栈空了 ,轮询开始,先轮询微任务队列,再轮询宏任务队列
微任务队列 | 宏任务队列 | console.log('promise2'); | console.log('end test1') | console.log("return test2 value"); | setTimeout(...) |
---|
8)执行顺序是先进先出
9)轮询微任务队列 输出 ’return test2 value‘ 、’end test1‘ 、 ’promise2‘
10)轮询宏任务队列 输出 ’setTimeout‘
"start test1"
"test2"
"start async"
"promise1"
"return test2 value"
"end test1"
"promise2"
"setTimeout"
同样的代码,在函数test2中, return await 'return test2 value' 事情会变成什么样?
async function test1() {
console.log('start test1');
console.log(await test2());
console.log('end test1');
}
async function test2() {
console.log('test2');
return await 'return test2 value'
}
test1();
console.log('start async');
setTimeout(() => {
console.log('setTimeout');
}, 0);
new Promise((resolve, reject) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
});
微任务队列 | 宏任务队列 | console.log('promise2'); | setTimeout(...) |
---|
再次模拟执行上面的代码
1)执行 test1 函数,输出‘statr test1’;
2)执行 console.log(await test2());执行 test2() 它是一个async函数,首先进入函数体输出 'test2' ;
但是由于函数 retrun 跟了一个 await ,因此这个代码没有执行完,回到 console.log(await test2()) 中,又是一个 await 因此也没有执行完,这段挂起了,意味着它阻塞了后面的代码,所以此时它返回的这个Promise 是处于pending 状态的;
相当于你在等你女朋友,你女朋友在等车 ,一切都还没有结果。
3)await 之后的代码也会被挂起 console.log('end test1') 挂起
4)执行console.log('start async'); 输出 'start async'
5)遇到 setTimeout(...) 进入宏任务队列
6)执行new Promise (..); 执行console.log('promise1') 输出 'promise1' ;
执行resolve(); 改变该Promise状态;.then()中的代码进入微任务队列
7)现在调用栈空了 ,轮询开始,先轮询微任务队列
8)执行顺序是先进先出
9)轮询微任务队列 输出 ’promise2‘
10)除挂起的代码外,别的都执行了一遍,回到刚刚挂起的地方 retrun await 'return test2 value'
11)'return test2 value' 作为 tese2()的返回值 成功返回了 ,现在console.log("return test2 value")是进入 Call stack,输出 return test2 value
12)console.log('end test1'); 进入call stack 输出 end test1
!11)和12)我无法确定是进入调用栈直接输出,还是进入微任务,然后再轮询后再输出,正常来说是进入微任务再轮询执行的,但是debug调试发现是直接输出的。
13)轮询 微任务队列 没有任务,接着轮询宏任务队列,有任务,执行 console.log('setTimeout'); 输出 ’setTimeout‘
start test1
test2
start async
promise1
promise2
return test2 value
end test1
setTimeout
来一张有趣的图 总结一下
1、async修饰符:async修饰的函数,默认返回 new Promise
对象的resolve
内容(若被async修饰的函数无返回值,则最终无返回值)。如此调用 async
修饰的函数,可以直接使用then
获取resolve
值,用catch
获取reject
值。
2、async方法内部,当程序执行到await方法时,会阻塞await方法后面的程序,进入await方法内部并执行到return前,然后跳出该async方法,执行与该async方法并列的同步任务。
3、await 后面若是 Promise 会暂停执行,直到Promise 返回结果才继续,若不是 Promise 会立刻返回
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!