Promise的微任务队列特性
Promise与微任务队列的关系
ECMAScript 6引入的Promise对象与JavaScript的事件循环机制紧密相关,特别是其回调处理被放入微任务队列而非宏任务队列。这种设计使得Promise回调的执行时机早于setTimeout等宏任务,即使它们是在同一事件循环中被创建。
console.log('Script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1');
}).then(() => {
console.log('Promise 2');
});
console.log('Script end');
// 输出顺序:
// Script start
// Script end
// Promise 1
// Promise 2
// setTimeout
微任务队列的执行机制
微任务队列在当前宏任务执行完毕后立即执行,且在渲染之前。这意味着微任务可以阻塞渲染,因为它们会在浏览器有机会绘制新帧之前全部执行完毕。每个微任务执行时都可能产生新的微任务,引擎会持续处理微任务队列直到队列为空。
function recursiveMicrotask(count = 0) {
if (count >= 3) return;
Promise.resolve().then(() => {
console.log(`Microtask ${count}`);
recursiveMicrotask(count + 1);
});
}
recursiveMicrotask();
setTimeout(() => console.log('Macrotask'), 0);
// 输出顺序:
// Microtask 0
// Microtask 1
// Microtask 2
// Macrotask
Promise链与微任务生成
每个Promise的then/catch/finally方法都会返回新的Promise,这些方法的回调函数会被作为微任务调度。当Promise状态改变时,所有相关的回调会被批量放入微任务队列。
const promise = new Promise((resolve) => {
console.log('Executor');
resolve('Initial');
});
promise
.then((value) => {
console.log('Then 1:', value);
return 'Modified';
})
.then((value) => {
console.log('Then 2:', value);
throw new Error('Failure');
})
.catch((error) => {
console.log('Catch:', error.message);
return 'Recovered';
})
.finally(() => {
console.log('Finally');
});
console.log('Sync code');
// 输出顺序:
// Executor
// Sync code
// Then 1: Initial
// Then 2: Modified
// Catch: Failure
// Finally
与async/await的交互
async函数本质上基于Promise,await表达式会暂停函数执行并将剩余代码包装为微任务。这使得async/await代码的执行顺序与Promise链类似,但语法更直观。
async function asyncFunc() {
console.log('Async start');
await Promise.resolve();
console.log('After await');
}
console.log('Script start');
asyncFunc();
console.log('Script end');
// 输出顺序:
// Script start
// Async start
// Script end
// After await
浏览器API中的微任务应用
许多现代浏览器API如MutationObserver、queueMicrotask等都使用微任务队列。理解Promise的微任务特性有助于正确处理这些API的交互。
// 使用queueMicrotask直接调度微任务
queueMicrotask(() => {
console.log('Microtask via queueMicrotask');
});
Promise.resolve().then(() => {
console.log('Microtask via Promise');
});
// 输出顺序:
// Microtask via Promise
// Microtask via queueMicrotask
性能考量与潜在问题
过度使用微任务可能导致主线程长时间阻塞,影响页面响应性。特别是在处理大量数据时,应考虑将任务分片或使用requestIdleCallback。
function processLargeData() {
const data = Array(10000).fill().map((_, i) => i);
// 不好的做法:一次性处理所有数据
// Promise.resolve().then(() => {
// data.forEach(heavyProcessing);
// });
// 更好的做法:分片处理
function processChunk(start) {
if (start >= data.length) return;
const end = Math.min(start + 100, data.length);
queueMicrotask(() => {
for (let i = start; i < end; i++) {
heavyProcessing(data[i]);
}
processChunk(end);
});
}
processChunk(0);
}
function heavyProcessing(item) {
// 模拟耗时操作
for (let i = 0; i < 100000; i++);
}
与其他异步模式的对比
与setImmediate、setTimeout(0)、requestAnimationFrame等调度方式相比,Promise微任务提供了更高优先级的执行时机,适合需要尽快执行但不阻塞UI更新的操作。
setTimeout(() => console.log('setTimeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
requestAnimationFrame(() => console.log('rAF'));
queueMicrotask(() => console.log('queueMicrotask'));
// 典型输出顺序:
// Promise
// queueMicrotask
// rAF
// setTimeout
Node.js环境下的差异
在Node.js中,process.nextTick的优先级甚至高于Promise微任务。此外,Node.js有独立的微任务队列实现,与浏览器环境略有不同。
process.nextTick(() => console.log('nextTick 1'));
Promise.resolve().then(() => console.log('Promise 1'));
process.nextTick(() => console.log('nextTick 2'));
Promise.resolve().then(() => console.log('Promise 2'));
// Node.js输出顺序:
// nextTick 1
// nextTick 2
// Promise 1
// Promise 2
实际应用场景分析
利用Promise的微任务特性可以实现高效的批量DOM操作、状态管理库的更新调度等场景。Vue.js等框架就利用此特性优化变更检测。
// 模拟状态管理库的批量更新
let isUpdating = false;
let pendingStates = [];
function setState(newState) {
pendingStates.push(newState);
if (!isUpdating) {
isUpdating = true;
Promise.resolve().then(() => {
const states = [...pendingStates];
pendingStates = [];
isUpdating = false;
applyStates(states);
});
}
}
function applyStates(states) {
console.log('Applying states:', states);
// 实际应用中这里会合并状态并更新组件
}
setState({ count: 1 });
setState({ count: 2 });
console.log('Sync code');
// 输出顺序:
// Sync code
// Applying states: [ { count: 1 }, { count: 2 } ]
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Promise的静态方法
下一篇:class关键字基本语法