Skip to content

Vue 数据响应任务队列

约 821 字大约 3 分钟

JavaScript

2025-03-31

Vue 的 queueJob(job) 机制确保了任务队列的 "单轮批量执行",并且 queue.clear() 只会在 flushJobs() 处理完当前队列 后执行,不会影响后续新加入的任务。


1. Vue 任务队列如何工作?

Vue 通过 queueJob(job) 添加 组件更新任务,并在 微任务阶段 统一执行 flushJobs() 处理队列:

let isFlushing = false;
let queue = new Set();

function queueJob(job) {
  queue.add(job); // 防止重复添加

  if (!isFlushing) {
    isFlushing = true;
    Promise.resolve().then(flushJobs); // 触发微任务
  }
}

function flushJobs() {
  isFlushing = false;
  queue.forEach(job => job()); // 执行所有任务
  queue.clear(); // 清空当前队列
}

核心机制:

  1. 每轮事件循环最多只触发一次 Promise.then(flushJobs)

    • isFlushing 防止多个 queueJob(job) 触发多个 flushJobs()
    • 这样 Vue 始终保证一个微任务处理所有任务,不会重复执行。
  2. queue.clear() 只清理当前队列,不影响新任务

    • queue.forEach(job => job()); 执行完当前队列的任务后,才执行 queue.clear()
    • 这个 clear() 不会影响后续 queueJob(job) 触发的新任务,因为它们会在 下一轮微任务 被处理。

2. 是否有可能导致丢失任务?

不会丢失任务,因为 Vue 任务队列是逐批执行的

情况 1:setter 触发大量 queueJob(),但仍然安全

for (let i = 0; i < 100; i++) {
  count.value++;
}

执行流程:

  1. 前 99 次 queueJob() 只会触发一个 Promise.then(flushJobs)
  2. flushJobs() 只会执行 最新的组件更新,不管 count.value++ 触发了多少次,最终 count.value 只更新 1 次(避免重复渲染)。
  3. queue.clear() 只清空 当前批次的 queue,不会影响后续的 queueJob(job)

情况 2:在 flushJobs() 运行时,又有新的 setter 触发

count.value++;
count.value++;

Promise.resolve().then(() => {
  count.value++; // 这里触发新的 queueJob()
});

执行顺序:

  1. count.value++ 触发 queueJob()queue 收集任务。
  2. Promise.then(flushJobs) 触发 第一轮批量更新
  3. flushJobs() 运行时,新的 count.value++ 触发 新的 queueJob()
  4. 由于 flushJobs() 结束时 isFlushing = false,Vue 会在 下一轮微任务 处理新的任务,不会丢失。

3. 什么时候可能出现潜在问题?

虽然 queue.clear() 本身不会导致问题,但如果 flushJobs() 内部代码有 同步代码阻塞,可能会延迟下一轮微任务触发:

function flushJobs() {
  isFlushing = false;
  queue.forEach(job => job());

  // ⚠️ 如果这里有同步阻塞代码,可能会影响 Vue 的微任务调度
  for (let i = 0; i < 1e9; i++) {} // 模拟耗时计算

  queue.clear();
}

在这种情况下:

  • queue.clear() 仍然只会清空当前批次,但同步阻塞代码可能会影响 下一轮任务触发,导致渲染延迟。
  • 解决方案是确保 flushJobs() 不会阻塞主线程,Vue 内部是不会有这种同步阻塞代码的。

4. 结论

queue.clear() 只清空当前批次的任务,不会影响后续 queueJob() 触发的新任务。 ✅ Vue 确保 flushJobs() 只会在微任务阶段执行一次,避免重复执行 & 渲染。 ✅ 不会丢失任务,即使在 flushJobs() 运行时有新的 queueJob() 触发,Vue 仍然会在 下一轮微任务阶段 处理。 ✅ 可能的风险是 如果 flushJobs() 内部有同步阻塞任务,会影响 Vue 的调度,但 Vue 内部避免了这种情况。

💡 总结:queue.clear() 在 Vue 的机制下是安全的,不会导致任务丢失! 🚀

更新日志

2025/6/15 11:14
查看所有更新日志
  • 13186-initial