众所周知,javascript的最大特点就是单线程,同一时间追能做同一件事,所以为了防止主线程的阻塞,在代码执行时分为同步任务和异步任务,所有的同步任务在主线程上执行,形成执行栈,而异步任务形成一个新的任务队列,又把任务队列中的异步任务分为宏任务和微任务,虽然他们都在任务队列中,但是它们却在不同的队列中,微任务的执行优先级大于宏任务,他们的结构如图所示。
浏览器为了能够使得JS内部任务与DOM任务能够有序的执行,会在一个任务执行结束后,在下一个任务执行开始前,对页面进行重新渲染
常见的宏任务主要有 定时器
,ajax
,读取文件
,dom事件
,setImmediate(Node.js 环境)
,requestAnimationFrame
,I/0
,UI交互
,postMessage
需要在当前 同步任务 执行结束后立即执行的任务,比如对一系列动作做出反馈,或者是需要异步的执行任务而又不需要分配一个新的任务,这样便可以减小一点性能的开销
常见的微任务包括Promise.then
,Object.observe
,MutationObserver
,process.nextTick(Node.js 环境)
1.定时器因为是异步宏任务,所以先执行主线程的打印语句,主线程没有任务再从宏任务任务队列中取出定时器执行
setTimeout(() => {
console.log("setTimeout qz");
}, 0);
console.log("qz");
//输出结果
//qz
//setTimeout qz
2.先执行主线程的同步任务,构造函数是同步任务,先打印aaa语句,在打印qz,主线程的语句没了,然后查看异步线程,promise.then是微任务,所以先打印.then和bbb,定时器宏任务最后执行
Promise.resolve().then((value) => console.log(".then"));
setTimeout(() => {
console.log("定时器");
}, 0);
new Promise((resolve) => {
console.log("aaa");
resolve();
}).then((res) => {
console.log("bbb");
});
console.log("qz");
//输出结果
//aaa
//qz
//.then
//bbb
//定时器
3,先执行主线程内容,打印qqzz,aaa,qz主线程没内容,开始执行异步队列中为微任务,打印.then,bbb,此时微任务没了,开始执行宏任务,打印定时器,因为定时器中含有微任务和宏任务,所以继续打印ccc,再执行定时器中的宏任务ddd,最后打印eee
Promise.resolve().then((value) => console.log(".then"));
setTimeout(() => {
console.log("定时器");
new Promise((resolve) => {
console.log("ccc");
resolve();
}).then((res) => {
console.log("ddd");
});
}, 0);
console.log("qqzz");
new Promise((resolve) => {
console.log("aaa");
setTimeout(() => {
console.log("eee");
}, 0);
resolve();
}).then((res) => {
console.log("bbb");
});
console.log("qz");
//输出结果
//qqzz
//aaa
//qz
//.then
//bbb
//定时器
//ccc
//ddd
//eee
1.主线程读取JS代码,环境为同步环境,将同步任务分为对应的堆和执行栈
2.同时,主线程执行中遇到异步任务,会将其推给异步进程进行处理,webAPI
3.异步任务对异步任务进行处理,遵循先进先出的顺序依次推入任务队列(异步队列)
4.主线程执行完同步队列之后,查询任务队列,按顺序执行微任务,待微任务执行完毕后执行宏任务
5.形成事件循环
执行顺序 : 同步任务 > process.nextTick > 微任务 > 宏任务 > seImmediate
hljs.highlightAll();