宏任务与微任务和浏览器的事件循环机制
Sep 21, 2022
# Browser
EventLoop 即事件循环,是一种解决 JavaScript 单线程运行时不会阻塞的一种机制
# 宏任务和微任务
宏任务包括:script(整体代码),I/O, setTimeout,setInterval,requestAnimationFrame,setImmediate。
其中setImmediate只存在于Node中,requestAnimationFrame只存在于浏览器中。
微任务包括: Promise,Object.observe(已废弃),MutationObserver(html5新特性),process.nextTick。
其中process.nextTick只存在于Node中,MutationObserver只存在于浏览器中。
UI Rendering不属于宏任务,也不属于微任务,它是一个与微任务平行的一个操作步骤
# 事件环分析练习
# 例1
js
// 设置trap
const bodyStyle = new Proxy(document.body.style, {
set(target, property, value, receiver) {
switch(property) {
case "backgroundColor":
target.backgroundColor = value;
console.log(`设置了颜色-${value}`);
break;
default:
break;
}
return true;
}
});
bodyStyle.backgroundColor = "orange";
console.log(1);
setTimeout(() => {
bodyStyle.backgroundColor = "green";
console.log(2);
}, 100);
Promise.resolve(3).then(num => {
bodyStyle.backgroundColor = "purple";
console.log(num);
});
console.log(4);
// 输出结果
// 设置了颜色-orange
// 1
// 4
// 设置了颜色-purple
// 3
// 设置了颜色-green
// 2
// (orange背景并没有渲染,因为在执行本轮微任务时被覆盖为了purple,微任务后进行UI渲染)
# 例2
js
Promise.resolve().then(() => {
console.log("p1");
setTimeout(() => {
console.log("s2");
}, 0);
});
setTimeout(() => {
console.log("s1");
Promise.resolve().then(() => {
console.log("p2");
});
}, 0);
// 第一圈:p1
// 第二圈:s1 p2
// 第三圈:s2
js
Promise.resolve().then(() => {
console.log("p1");
setTimeout(() => {
console.log("s2");
}, 0);
setTimeout(() => {
console.log("s3");
}, 0);
});
setTimeout(() => {
console.log("s1");
Promise.resolve().then(() => {
console.log("p2-1");
}).then(() => {
console.log("p2-2");
});
}, 0);
// 第一圈:p1
// 第二圈:s1 p2-1 p2-2
// 第三圈:s2
// 第四圈:s3
# 例3
js
console.log(1);
setTimeout(() => {
console.log(2);
});
new Promise((resolve, reject) => {
console.log(3);
resolve("");
console.log(4);
}).then((res) => {
console.log(5);
});
console.log(6);
// 1 3 4 6
// 5
// 2
// 异步任务 异步任务
// 同步代码 -> 微任务代码 -> 渲染 -> 宏任务代码
js
console.log(1);
setTimeout(() => {
Promise.resolve().then(() => {
console.log("p2");
})
}, 10); // 10ms延迟
new Promise((resolve, reject) => {
console.log(3);
resolve("");
console.log(4);
}).then(() => {
setTimeout(() => {
console.log("p1");
}, 0);
});
console.log(6);
// 1 3 4 6
// p1
// p2
# 例4
js
let res = function() {
console.log(1);
return new Promise((resolve, reject) => {
console.log(2);
resolve(4);
});
}
new Promise(async (resolve, reject) => {
console.log(3);
let test = await res();
console.log(test);
});
console.log(5);
new Promise((resolve, reject) => {
console.log(6);
});
console.log(7);
// 同步任务:3 1 2 5 6 7
// 微任务:4
js
let res = function() {
console.log(1);
return new Promise((resolve, reject) => {
// setTimeout2
setTimeout(() => {
// p3
new Promise((resolve) => {
console.log(2);
// setTimeout5
setTimeout(() => {
console.log(3);
}, 0);
});
}, 0);
resolve(5);
});
}
new Promise(async (resolve, reject) => {
// setTimeout1
setTimeout(() => {
// p2
Promise.resolve().then(() => {
console.log(4);
});
}, 0);
// p1
let test = await res();
console.log(test);
});
// setTimeout3
setTimeout(() => {
console.log(6);
}, 0);
new Promise((resolve, reject) => {
// setTimeout4
setTimeout(() => {
console.log(7);
}, 0);
});
console.log(8);
// 1 8 5 4 2 6 7 3
// 同步执行:1 8 清空微任务 > p1:5
// setTimeout1 清空微任务 > p2:4
// setTimeout2:2
// setTimeout3:6
// setTimeout4:7
// setTimeout5:3
# 例5
js
const btn = document.getElementById("btn");
// 宏任务:事件处理函数的回调
btn.addEventListener("click", () => {
console.log(1);
Promise.resolve("m1").then((str) => {
console.log(str);
});
}, false);
btn.addEventListener("click", () => {
console.log(2);
Promise.resolve("m2").then((str) => {
console.log(str);
});
}, false);
btn.click();
// 1 2
// m1 m2
// 当用户手动点击click按钮的时候,输出结果
// 1 m1
// 2 m2
用户手动点击时进行了两次事件循环,而程序调用方法btn.click()
时等同于执行下面的代码。
js
const handler1 = () => {
console.log(1);
Promise.resolve("m1").then(str => {
console.log(str);
});
}
const handler2 = () => {
console.log(2);
Promise.resolve("m2").then(str => {
console.log(str);
});
}
// handler1,handle2的执行是同步代码,同步代码执行完后清空两个微任务
handler1();
handler2();
// 1 2
// m1 m2
js
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
setTimeout(() => {
console.log(1);
});
Promise.resolve("m1").then((str) => {
console.log(str);
});
}, false);
btn.addEventListener("click", () => {
setTimeout(() => {
console.log(2);
});
Promise.resolve("m2").then((str) => {
console.log(str);
});
}, false);
btn.click();
// m1 m2
// 1 2
# 例6
js
console.log("start");
const interval = setInterval(() => {
console.log("setInterval");
}, 0);
setTimeout(() => {
console.log("setTimeout1");
Promise.resolve()
.then(() => {
console.log("p3");
})
.then(() => {
console.log("p4");
})
.then(() => {
setTimeout(() => {
console.log("setTimeout2");
Promise.resolve()
.then(() => {
console.log("p5");
})
.then(() => {
console.log("p6");
})
.then(() => {
clearInterval(interval);
});
}, 0);
});
}, 0);
Promise.resolve()
.then(() => {
console.log("p1");
})
.then(() => {
console.log("p2");
});
// start p1 p2
// setInterval
// setTimeout1 p3 p4
// setInterval
// setTimeout2 p5 p6
// 更新后的chrome,没有了setInterval的输出
//(客观感受是setInterval的优先级低于了setTimeout,导致还没输出就已经被清除了)
// start p1 p2
// setTimeout1 p3 p4
// setTimeout2 p5 p6
# 例7
js
setTimeout(() => {
console.log('setTimeout1');
setTimeout(() => {
console.log('setTimeout3');
}, 1000);
Promise.resolve().then(() => {
console.log('then3');
});
}, 1000);
Promise.resolve().then(() => {
console.log('then1');
console.log('then4');
Promise.resolve().then(() => console.log('then6'));
});
Promise.resolve().then(() => {
console.log('then2');
console.log('then5');
setTimeout(() => {
console.log('setTimeout2');
}, 1000);
});
// then1 then4 then2 then5 then6 本轮循环产生的微任务都会在本次循环清空
// setTimeout1 then3
// setTimeout2
// setTimeout3
# 例8
js
setTimeout(() => {
console.log(1);
}, 0);
new Promise((resolve) => {
console.log(2);
resolve();
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
});
console.log(6);
// 2 6 3 4
// 1
# 例9
js
console.log(1);
setTimeout(() => {
console.log(2);
new Promise((resolve) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
});
});
new Promise((resolve) => {
console.log(5);
resolve();
}).then(() => {
console.log(6);
})
setTimeout(() => {
console.log(7);
});
setTimeout(() => {
console.log(8);
new Promise((resolve) => {
console.log(9);
resolve();
}).then(() => {
console.log(10);
});
});
new Promise((resolve) => {
console.log(11);
resolve();
}).then(() => {
console.log(12);
});
console.log(13);
// 1 5 11 13 6 12
// 2 3 4
// 7
// 8 9 10
# 例10
js
async function async1 () {
console.log("a1-start");
await async2();
console.log("a1-end");
}
async function async2 () {
console.log("async2");
}
console.log("start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
async1();
new Promise((resolve) => {
console.log("p1");
resolve();
}).then(() => {
console.log("p2");
});
console.log("end");
// start a1-start async2 p1 end a1-end p2
// setTimeout
js
async function async1 () {
console.log("a1-start");
await async2();
console.log("a1-end");
}
async function async2 () {
new Promise((resolve) => {
console.log("p1");
resolve();
}).then(() => {
console.log("p2");
});
}
console.log("start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
async1();
new Promise((resolve) => {
console.log("p3");
resolve();
}).then(() => {
console.log("p4");
});
console.log("end");
// start a1-start p1 p3 end p2 a1-end p4
// setTimeout
js
async function async1 () {
console.log("a1-start");
await async2();
/**
* awaitPromsie
* async2().then(() => {
* setTimeout(() => {
* console.log("setTimeout1");
* })
* })
*/
setTimeout(() => {
console.log("setTimeout1");
});
}
async function async2 () {
setTimeout(() => {
console.log("setTimeout2");
}, 0);
}
console.log("start");
setTimeout(() => {
console.log("setTimeout3");
}, 0);
async1();
new Promise((resolve) => {
console.log("p1");
resolve();
}).then(() => {
console.log("p2");
});
console.log("end");
// start a1-start p1 end p2
// setTimeout3
// setTimeout2
// setTimeout1
# 例11
js
var promise = new Promise((resolve) => {
console.log(1);
resolve();
});
setTimeout(() => {
console.log(2);
});
promise.then(() => {
console.log(3);
});
var promise2 = getPromise();
async function getPromise () {
console.log(5);
await promise;
console.log(6);
}
console.log(8);
// 1 5 8 3 6
// 2
# 例12
js
const LazyMan = function (name) {
console.log(`Hi i am ${ name }`);
function _eat (food) {
console.log(`I am eating ${ food }`);
}
const callbacks = [];
class F {
sleep (timeout) {
setTimeout(() => {
console.log(`等待了${ timeout }秒...`);
callbacks.forEach(cb => cb());
}, timeout * 1000);
return this;
}
eat (food) {
callbacks.push(_eat.bind(null, food));
return this;
}
}
return new F();
}
LazyMan("Tony").sleep(5).eat("lunch").eat("fish");
// Hi i am Tony
// 等待了5秒... I am eating lunch I am eating lunch