闽公网安备 35020302035485号
| 角色 | 类比 |
|---|---|
| 调用栈 Call Stack | 烤串师傅(一次只能烤一串) |
| 宏任务队列 Macrotask Queue | 外卖订单(setTimeout、I/O、UI 渲染) |
| 微任务队列 Microtask Queue | 加急小票(Promise、queueMicrotask、MutationObserver) |
document.getElementById('saveButton').addEventListener('click', async () => {
updateLoadingState(true); // 1. 立刻转菊花
try {
await saveUserData(userData); // 2. 异步保存
showSuccessMessage('保存成功'); // 3. 微任务里更新 UI
const data = await fetchUser(); // 4. 再拿新数据
updateUI(data); // 5. 再更新 UI
} catch (e) {
showErrorMessage(e.message); // 6. 出错兜底
} finally {
updateLoadingState(false); // 7. 关闭菊花
}
});
菊花之所以能立刻转起来,是因为 updateLoadingState(true) 是同步的。queueMicrotask(() => {
console.log('我是加急,宏任务都靠边!');
});
与 setTimeout(fn, 0) 的区别:setTimeout 是宏任务,得等渲染;queueMicrotask 是微任务,马上跑。class ThemeManager {
constructor() {
this.theme = { darkMode: false, contrast: 'normal', fontSize: 16 };
this.isRendering = false;
}
// 堆代码 duidaima.com
updateTheme(updates) {
// 1. 改数据
this.theme = { ...this.theme, ...updates };
// 2. 只排一次队,避免多次重排
if (!this.isRendering) {
this.isRendering = true;
queueMicrotask(() => {
this.applyThemeToDOM();
this.isRendering = false;
});
}
}
applyThemeToDOM() {
document.documentElement.style.setProperty(
'--bg',
this.theme.darkMode ? '#1a1a1a' : '#fff'
);
}
}
const tm = new ThemeManager();
tm.updateTheme({ darkMode: true });
tm.updateTheme({ contrast: 'high' });
tm.updateTheme({ fontSize: 20 });
// 最终只调用一次 applyThemeToDOM,性能拉满!
FormController:表单提交不抖屏class FormController {
constructor(formEl) {
this.form = formEl;
this.isSubmitting = false;
this.form.addEventListener('submit', async (e) => {
e.preventDefault();
if (this.isSubmitting) return;
this.setSubmitting(true);
try {
const res = await fetch('/api/submit', { method: 'POST', body: new FormData(this.form) });
if (!res.ok) throw new Error(res.statusText);
// 用微任务确保状态更新后再提示用户
queueMicrotask(() => {
this.showSuccess();
this.resetForm();
});
} catch (err) {
this.showError(err);
} finally {
this.setSubmitting(false);
}
});
}
setSubmitting(flag) {
this.isSubmitting = flag;
this.form.querySelector('button[type="submit"]').disabled = flag;
}
}
微任务 vs Promise:谁先谁后?console.log('Script start');
setTimeout(() => console.log('setTimeout'), 0);
queueMicrotask(() => {
console.log('Microtask 1');
queueMicrotask(() => console.log('Nested Microtask 1'));
});
Promise.resolve()
.then(() => {
console.log('Promise 1');
queueMicrotask(() => console.log('Nested Microtask 2'));
})
.then(() => console.log('Promise 2'));
queueMicrotask(() => console.log('Microtask 2'));
console.log('Script end');
/*
输出顺序:
Script start
Script end
Microtask 1
Promise 1
Microtask 2
Nested Microtask 1
Promise 2
Nested Microtask 2
setTimeout
*/
口诀:“清完微任务,才轮到宏任务”。function infiniteMicrotask() {
queueMicrotask(infiniteMicrotask); // 卡死循环,浏览器直接罢工
}
2. 微任务里干重活 → 阻塞渲染queueMicrotask(() => {
// 千万别算大斐波那契,UI 会冻成狗
});
3. 与宏任务混用 → 顺序谜之混乱| 特性 | JS 微任务 | Go defer |
|---|---|---|
| 触发时机 | 当前同步栈清空后 | 函数 return 前 |
| 执行顺序 | FIFO(队列) | LIFO(栈) |
| 异步? | ✅ 是 | ❌ 否 |
| 典型用途 | UI 批处理/状态同步 | 文件关闭/锁释放 |
3.断点:直接在 .then 或 queueMicrotask 回调里打断点,看闭包变量。
.时间敏感动画(交给 requestAnimationFrame)