
setTimeout(()=> {
console.log('setTimeout 1000ms')
},1000)
let i = 0;
let start = Date.now();
function count() {
// 做一个繁重的任务
for (let j = 0; j < 1e9; j++) {
i++;
}
console.log("Done in " + (Date.now() - start) + 'ms');
}
count()
/*
第一个方法: 异步函数,回调会在1秒后执行
第二个方法: count函数会在1.5秒后执行完毕
结果:
Done in 1555ms
setTimeout 1000ms
*/
宏任务和微任务
宏任务与微任务有哪些
宏任务:
script(整体代码)setTimeout/setInterval/setImmediate回调I/O *** 作(ajax)用户事件触发(click/onmousemove...)微任务:
Promise.resolve().then(...) Vue源码中关于vdom使用的 *** 作MutationObserver process.nextTick(Node.js)这是我将宏任务与微任务与同步异步概念分开的原因,因为异步可能是宏任务也可能是微任务,而宏任务可能是异步代码也可能是同步代码,所以如果强行将两者结合理解,会造成不必要的理解障碍。
事件循环与宏/微任务 相比于同步异步方法,宏任务和微任务能更好的描述事件循环,可以通过此网站更好地理解宏任务与微任务,神站也。浏览器的渲染机制是,在当前宏任务执行完成之前,会清空所有微任务,并在下一个宏任务开始之前进行渲染 *** 作。以下代码运行和解析可以帮助你更好地理解事件循环:
$('#progress')[0].innerHTML = 'script 1';
setTimeout(()=>{
$('#progress')[0].innerHTML = 'setTimeout 2';
}, 3000)
Promise.resolve().then(function () {
$('#progress')[0].innerHTML = 'script 2';
})
setTimeout(()=>{
$('#progress')[0].innerHTML = 'setTimeout 1';
}, 2000)
1. script宏任务入任务队列,首先执行dom *** 作,但因为此时script宏任务还未执行完毕,并未渲染
2. setTimeout执行,回调将会在3秒后以宏任务状态入任务队列
3. Promise执行,then回调入微任务队列
4. setTimeout执行,回调将会在2秒后以宏任务状态入任务队列
5. script宏任务即将结束,即将清空微任务队列
6. 清空微任务队列,执行dom *** 作
7. script宏任务执行结束,以script 2渲染
8. 从任务一开始的2秒后 第二个setTimeout回调推入任务队列-宏任务,此时无任何微任务和其他响应事件,直接运行,以setTimeout 1渲染
9. 结束第二个setTimeout回调的宏任务
10. 从任务一开始的3秒后 第一个setTimeout回调推入任务队列-宏任务,此时无任何微任务和其他响应事件,直接运行,以setTimeout 2渲染
11. 结束所有事件
事件循环初步解析我们知道,以下代码中的dom是不会实时跟随js *** 作更新的,只会在js循环结束后进行更新渲染。
setTimeout(()=>{
console.log(123)
})
let start = Date.now();
// 假设这是个耗时比较长的 *** 作
for (let i = 0; i < 1e5; i++) {
$('#progress')[0].innerHTML = "Now is " + (Date.now() - start);
}
因为for循环做为script同步代码,必须等待所有 *** 作结束才能执行下一个任务,假设此时运行一个耗时很长的复杂任务,这时候又接连产生其他任务如:用户事件 *** 作、异步回调等,这些其他任务会被排入队列,等待引擎处理完复杂任务之后,再根据先进先出原则继续处理事件。那么上述代码处运行程为:
1. 首先script宏任务入队列,setTimeout回调函数作为另一个宏任务在经过回调时间后(图中代码为0s,但是一般有4ms延时)立即排入队列,但是排在script任务后面
2. 继续执行后续script任务,在执行大概1000ms后结束任务
3. script任务执行完毕之后,浏览器进行渲染 *** 作
4. 运行setTimeout回调,打印日志
那么我们将如何通过利用宏/微任务来解决上述问题并延伸应用场景?
微任务/宏任务应用场景宏任务
任务拆分如下代码所示,count方法总运行时间为循环1e7的运行时间,通过setTimeout拆分成若干以1e4循环时间为单位等分。于是在每个1e4时间间隔内,我们都可以通过穿插宏任务来执行我们想要进行的 *** 作。(穿插的宏任务分为主动类型,如用户点击响应事件等,被动类型,如setTimeout在经过一段事件后的入队回调函数等。)
setTimeout(()=> {
alert('setTimeout')
},1000)
let i = 0;
function count() {
do {
i++;
$('#progress')[0].innerHTML = i;
}
while (i % 1e4 != 0);
if (i < 1e7) {
setTimeout(count)
}
}
count()
进度条显示
上述代码中,会以每1e4循环时间为单位对$('#progress')进行一次渲染,那我们就可以根据我们所需要的等分条件进行 *** 作dom进度条显示。注意,我们之前提到,setTimeout有大概4ms的延时,所以我们等分单位越小,时间会越长;当然我们也可以稍微优化下将setTimeout提前至count方法开始处。
微任务
Vue源码中对于渲染更新的应用vue会在浏览器渲染之前,统一处理所有的 *** 作,一次性生成虚拟dom后再转为真实dom
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)