目录
禁止跨线程访问DOM:webworker线程不能直接操作DOM
宏任务macrotask: setTimeout,setInterval定时事件,Ajax,DOM事件,script 脚本的执行、 I/O 操作、UI 渲染等。
优先级:js>setImmediate(Node环境)>setTimeout
微任务microtask(异步):Promise 、async/await、alert、MutationObserver,process.nextTick,queueMicrotask
优先级:process.nextTick>其他微任务>alert>Promise=async/await
async隐式返回Promise,会产生一个微任务await xx;后的代码在微任务时执行
promise>MutationObserver>setImmediate>setTimeout
setTimeout(delay=0)=setImmediate:下个Event Loop执行
竞态条件(race condition)和死锁(deadlock):一个删除DOM,另外一个访问DOM
安全性:JavaScript主要是在浏览器端执行,多线程的设计可能引发安全隐患,例如跨线程访问共享数据容易导致数据不一致或非法访问。
同步模型:JavaScript的单线程模型使得事件驱动和回调机制相对容易实现。当有事件触发时,JavaScript将该事件添加到事件队列中,然后按照顺序依次执行队列中的事件回调函数。这种机制使得处理异步操作、定时器和用户交互事件等变得简单。
但是一些高耗时操作就带来了进程阻塞问题。为了解决这个问题,Js 有两种任务的执行模式:同步模式(Synchronous)和异步模式(Asynchronous)
优先级:js>setImmediate(Node环境)>setTimeout优先级:process.nextTick>其他微任务>alert>Promise=async/await MutationObserver 是浏览器提供的一个用于监测 DOM 变化的接口。它允许开发者在 DOM 树发生变化时进行异步处理。MutationObserver 可以观察到节点的增删改等操作
- // 创建一个 MutationObserver 实例,指定回调函数
- const observer = new MutationObserver((mutations, observer) => {
- mutations.forEach((mutation) => {
- // 处理每个变化(mutation)
- console.log(mutation.type); // 变化的类型(attributes、childList、characterData)
- console.log(mutation.target); // 受影响的节点
- });
- });
-
- // 配置观察选项
- const config = {
- attributes: true, // 观察属性的变化
- childList: true, // 观察子节点的变化
- subtree: true, // 观察所有后代节点的变化
- characterData: true, // 观察字符数据的变化
- // 其他配置项...
- };
-
- // 开始观察目标节点
- observer.observe(targetNode, config);
-
- // 在不需要观察时,可以调用 disconnect 方法停止观察
- // observer.disconnect();
-
- //1.script start(同步)
- console.log("script start");
-
- async function async1() {
- await async2(); // await 隐式返回promise
- console.log("async1 end"); // 这里的执行时机:在执行微任务时执行
- }
-
- async function async2() {
- console.log("async2 end"); // 这里是同步代码
- }
- //2.async2 end(同步)
- //微任务队列:[async1 end]
- async1();
- //宏任务队列:[setTimeout],setTimeOut进入下一loop
- setTimeout(function() {
- console.log("setTimeout");
- }, 0);
- //3.Promise(同步)
- //宏任务队列:[setTimeout]
- //微任务队列:[async1 end,promise1]
- new Promise(resolve => {
- console.log("Promise"); // 这里是同步代码
- resolve();
- })
- .then(function() {
- console.log("promise1");
- })
- .then(function() {
- console.log("promise2");
- });
- //4.script end(同步)
- console.log("script end");
- //当前loop的宏任务(都是同步代码)都执行完毕
- //执行所有微任务[async1 end,promise1]
- //执行promise1完后碰到了promise2,加入微任务队列,接着执行
- //当前所有微任务都执行完毕,开始执行宏任务队列[setTimeout]
-
- // 打印结果: script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout
setTimeoutPromise:如果浏览器支持Promise,nextTick会优先使用Promise.then来创建微任务,以确保回调函数在下一个微任务队列中执行。
MutationObserver:如果浏览器不支持Promise,nextTick会检查是否支持MutationObserver。MutationObserver变动观察器)是一种Web API,它允许开发者监视DOM树的变化并在这些变化发生时执行回调函数允许监视DOM树的变化,因此它也可以用于异步任务的调度。nextTick会尝试使用MutationObserver来创建微任务。
setImmediate:如果浏览器既不支持Promise也不支持MutationObserver,nextTick会检查是否支持setImmediate。setImmediate是一种宏任务,通常比setTimeout执行得更早,因此它用于创建宏任务级别的异步任务。
setTimeout:如果以上方法都不可用,nextTick会回退到使用setTimeout来创建异步任务。setTimeout是一种宏任务,但是优先级较低,可能在其他异步任务之后执行。
- // 定义nextTick的回调队列
- let callbacks = [];
-
- // 批量执行nextTick的回调队列
- function flushCallbacks() {
- callbacks.forEach((cb) => cb());
- callbacks = [];
- pending = false;
- }
-
- //定义异步方法,优先使用微任务实现
- let timerFunc;
-
- // 优先使用promise 微任务
- if (Promise) {
- timerFunc = function () {
- return Promise.resolve().then(flushCallbacks);
- };
- // 如不支持promise,再使用MutationObserver 微任务
- } else if (MutationObserver) {
- timerFunc = function () {
- const textNode = document.createTextNode('1');
- const observer = new MutationObserver(() => {
- flushCallbacks();
- observer.disconnect();
- });
- const observe = observer.observe(textNode, { characterData: true });
- textNode.textContent = '2';
- };
- // 微任务不支持,再使用宏任务实现
- } else if (setImmediate) {
- timerFunc = function () {
- setImmediate(flushCallbacks);
- };
- } else {
- timerFunc = function () {
- setTimeout(flushCallbacks);
- };
- }
-
- // 定义nextTick方法
- export function nextTick(cb) {
- callbacks.push(cb);
- if (!pending) {
- pending = true;
- timerFunc();
- }
- }
- console.log("start");
- //定时进入下一loop,宏任务队列:[timeout]
- setTimeout(() => {
- console.log("timeout");
- }, 0);
- //微任务队列:[promise]
- Promise.resolve().then(() => {
- console.log("promise");
- });
- //process.nextTick在微任务队首
- //微任务队列:[nextTick,promise]
- process.nextTick(() => {
- console.log("nextTick");
- Promise.resolve().then(() => {
- console.log("promise1");
- });
- });
- console.log("end");
- // 执行结果 start end nextTick promise promise1 timeout

Event Loop执行- //宏任务队列:[]
- //微任务队列:[promise0]
- Promise.resolve()
- .then(function() {
- console.log("promise0");
- })
- .then(function() {
- console.log("promise5");
- });
- //定时的setTimeout(delay=0)=setImmediate:下个Event Loop执行
- //宏任务队列:[timer1]
- //微任务队列:[promise0]
- setTimeout(() => {
- console.log("timer1");
-
- Promise.resolve().then(function() {
- console.log("promise2");
- });
- Promise.resolve().then(function() {
- console.log("promise4");
- });
- }, 0);
- //宏任务队列:[timer1,timer2]
- //微任务队列:[promise0]
- setTimeout(() => {
- console.log("timer2");
- Promise.resolve().then(function() {
- console.log("promise3");
- });
- }, 0);
- //宏任务队列:[timer1,timer2]
- //微任务队列:[promise0,promise1]
- Promise.resolve().then(function() {
- console.log("promise1");
- });
- //执行start
- console.log("start");
- //执行当前所有微任务队列:[promise0,promise1]
- //执行promise0时将promise5放入了微任务队列:[promise1,promise5]
- //接着执行微任务队列:输出promise1,promise5
- //当微任务队列为空,开始执行宏任务队列[timer1,timer2]队首的timer1
- //执行timer1时碰到了微任务promise2,放进微任务队列[promise2]
- //宏任务timer1执行完了,开始执行所有当前所有微任务:[promise2]
- //执行promise2完碰到微任务promise4,放进微任务队列:[promise4]
- //当前微任务队列不为空,接着执行promise4
- //微任务队列为空,接着执行宏任务队列队首[timer2]
- //执行timer2时碰到了微任务promise3,放进微任务队列[promise3]
- //宏任务timer2执行完了,开始执行所有当前所有微任务:[promise3]
-
-
- // 打印结果: start promise0 promise1 promise5 timer1 promise2 promise4 timer2 promise3
宏任务 → 微任务 → 渲染更新浏览器更新渲染会在event loop中的 宏任务 和 微任务 完成后进行,即宏任务 → 微任务 → 渲染更新(先宏任务 再微任务,然后再渲染更新)
宏任务队列中,如果有大量任务等待执行时,将dom的变动作为微任务,能更快的将变化呈现给用户,这样就可以在这一次的事件轮询中更新dom