- 主线程中的任务执行完后,才执行任务队列中的任务
- 有新任务到来时会将其放入队列,采取先进先执行的策略执行队列中的任务
- 比如多个
setTimeout
同时到时间了,就要依次执行
任务包括 script(整体代码)、 setTimeout、setInterval、DOM渲染、DOM事件、Promise、XMLHTTPREQUEST等
原理分析
通过一个例子来详细分析宏任务与微任务
console.log("后盾人");
setTimeout(function() {
console.log("定时器");
}, 0);
Promise.resolve()
.then(function() {
console.log("promise1");
})
.then(function() {
console.log("promise2");
});
console.log("houdunren.com");
#输出结果为
后盾人
houdunren.com
promise1
promise2
定时器
先执最前面的宏任务 script,然后输出
然后执行到setTimeout 异步宏任务,并将其放入宏任务队列,等待执行 之后执行到 Promise.then 微任务,并将其放入微任务队列,等待执行 然后执行到主代码输出
script start
主线程所有任务处理完成
script end
通过事件循环遍历微任务队列,将刚才放入的Promise.then微任务读取到主线程执行,然后输出
promise1
之后又执行 promse.then 产生新的微任务,并放入微任务队列
主线程任务执行完毕
现次事件循环遍历微任务队列,读取到promise2微任务放入主线程执行,然后输出
主线程任务执行完毕
promise2
此时微任务队列已经无任务,然后从宏任务队列中读取到 setTimeout任务并加入主线程,然后输出
setTimeout
定时器
定时器会放入异步任务队列,也需要等待同步任务执行完成后执行。
下面设置了 6 毫秒执行,如果主线程代码执行10毫秒,定时器要等主线程执行完才执行。
HTML标准规定最小时间不能低于4毫秒,有些异步操作如DOM操作最低是16毫秒,总之把时间设置大些对性能更好。
setTimeout(func,6);
下面的代码会先输出 houdunren.com
之后输出 后盾人
setTimeout(() => {
console.log("后盾人");
}, 0);
console.log("houdunren.com");
这是对定时器的说明,其他的异步操作如事件、XMLHTTPREQUEST 等逻辑是一样的
微任务
微任务一般由用户代码产生,微任务较宏任务执行优先级更高,Promise.then
是典型的微任务,实例化 Promise 时执行的代码是同步的,便then注册的回调函数是异步微任务的。
任务的执行顺序是同步任务、微任务、宏任务所以下面执行结果是 1、2、3、4
setTimeout(() => console.log(4));
new Promise(resolve => {
resolve();
console.log(1);
}).then(_ => {
console.log(3);
});
console.log(2);
再来看下面稍复杂的任务代码
setTimeout(() => {
console.log("定时器");
setTimeout(() => {
console.log("timeout timeout");
}, 0);
new Promise(resolve => {
console.log("settimeout Promise");
resolve();
}).then(() => {
console.log("settimeout then");
});
}, 0);
new Promise(resolve => {
console.log("Promise");
resolve();
}).then(() => {
console.log("then");
});
console.log("后盾人");
实例操作
优化思想
一个比较耗时的任务可能造成游览器卡死现象,所以可以将任务拆分为多个异步小任务执行。下面是一个数字统计的函数,我们会发现运行时间特别长
console.time("runtime");
function hd(num) {
let count = 0;
for (let i = 0; i <= num; i++) {
count += i;
}
console.log(count);
console.timeEnd("runtime");
}
let num=987654321;
hd(num);
console.log("houdunren.com"); //需要等待上面执行完才会执行
现在把任务分解成小块放入任务队列,游览器就不会出现卡死的现象了,也不会影响后续代码的执行
console.time("runtime");
let count = 0;
let num = 987654321;
function hd() {
for (let i = 0; i < 100000000; i++) {
if (num <= 0) break;
count += num--;
}
if (num > 0) {
console.log(num);
setTimeout(hd);
} else {
console.log(num);
console.log(count);
}
}
hd();
console.log("houdunren.com"); //立刻显示出来
交给微任务处理是更好的选择
async function hd(num) {
let res = await Promise.resolve().then(_ => {
let count = 0;
for (let i = 0; i < num; i++) {
count += num--;
}
return count;
});
console.log(res);
}
hd(987654321);
console.log("后盾人");