一、前言
js任务分为同步任务 和异步任务 ,异步任务又分为宏任务和微任务,其中异步任务属于耗时的任务。
二、宏任务和微任务有哪些?
宏任务:整体代码script 、setTimeout 、setInterval 、setImmediate(Node.js) 、i/o操作(输入输出,比如读取文件操作、网络请求) 、ui render(dom渲染,即更改代码重新渲染dom的过程) 、异步ajax等
微任务:Promise(then、catch、finally) 、async/await 、process.nextTick(Node.js) 、Object.observe (⽤来实时监测js中对象的变化,已废弃 )、 MutationObserver(监听DOM树的变化)
setTimeout
大名鼎鼎的setTimeout无需再多言,大家对他的第一印象就是异步可以延时执行,我们经常这么实现延时3秒执行:
setTimeout(() => {
console.log('延时3秒');
},3000)
我们还经常遇到setTimeout(fn,0)
这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢? 答案是不会的,setTimeout(fn,0)
的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。举例说明:
//代码1
console.log('先执行这里');
setTimeout(() => {
console.log('执行啦')
},0);
//代码2
console.log('先执行这里');
setTimeout(() => {
console.log('执行啦')
},3000);
代码1的输出结果是:
先执行这里
执行啦
代码2的输出结果是:
//先执行这里
// ... 3s later
// 执行啦
关于setTimeout要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。有兴趣的同学可以自行了解。
Promise与process.nextTick(callback)
- Promise的定义和功能本文不再赘述,可以学习一下 阮一峰老师的Promise
- 而process.nextTick(callback)类似node.js版的"setTimeout",在事件循环的下一次循环中调用 callback 回调函数。
看例子:
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,会在同步任务执行完后会去清空microtasks queues, 最后清空完微任务再去宏任务队列取值。
Promise.resolve().then(()=>{
console.log('Promise1')
setTimeout(()=>{
console.log('setTimeout2')
},0)
})
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
},0)
这回是嵌套,大家可以看看,最后输出结果是Promise1,setTimeout1,Promise2,setTimeout2
- 一开始执行栈的同步任务执行完毕,会去 microtasks queues 找 清空 microtasks queues ,输出
Promise1
,同时会生成一个异步任务 setTimeout1 - 去宏任务队列查看此时队列是 setTimeout1 在 setTimeout2 之前,因为setTimeout1执行栈一开始的时候就开始异步执行,所以输出
setTimeout1
- 在执行setTimeout1时会生成Promise2的一个 microtasks ,放入 microtasks queues 中,接着又是一个循环,去清空 microtasks queues ,输出
Promise2
- 清空完 microtasks queues ,就又会去宏任务队列取一个,这回取的是
setTimeout2
三、执行顺序
js代码在执行的时候,会先执行同步代码 ,遇到异步宏任务则将异步宏任务放入宏任务队列中 ,遇到异步微任务则将异步微任务放入微任务队列中 ,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后,再将异步宏任务从队列中调入主线程执行,一直循环至所有的任务执行完毕(完成一次事件循环EventLoop)。
题目练习:
练习一
setTimeout(function () {
console.log('1');
})
new Promise(function (resolve) {
console.log('2');
resolve();
}).then(function () {
console.log('3');
})
console.log('4');
//打印顺序 2 4 3 1
练习二
console.log(1);
setTimeout(function () {
console.log(2);
let promise = new Promise(function (resolve, reject) {
console.log(3);
resolve();
}).then(function () {
console.log(4);
});
}, 1000);
setTimeout(function () {
console.log(5);
let promise = new Promise(function (resolve, reject) {
console.log(6);
resolve();
}).then(function () {
console.log(7);
});
}, 0);
let promise = new Promise(function (resolve, reject) {
console.log(8);
resolve()
}).then(function () {
console.log(9);
}).then(function () {
console.log(10)
});
console.log(11);
//执行顺序:1 8 11 9 10 5 6 7 2 3 4