- 主线程中的任务执行完后,才执行任务队列中的任务
- 有新任务到来时会将其放入队列,采取先进先执行的策略执行队列中的任务
- 比如多个
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.compromise1promise2定时器
先执最前面的宏任务 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("后盾人");
