Optimization of microtasks and macrotasks
Microtasks and Macrotasks Optimization
Microtasks and macrotasks are core concepts in JavaScript's event loop, crucial for performance optimization. Understanding their execution order and differences can effectively prevent code blocking and improve rendering efficiency, especially in complex interaction scenarios.
Event Loop Basics
JavaScript adopts a single-threaded model, handling asynchronous operations through the event loop mechanism. When the call stack is empty, the event loop retrieves tasks from the task queue for execution. Tasks are divided into two categories:
- Macrotasks: Includes entire script code, setTimeout, setInterval, I/O operations, UI rendering, etc.
- Microtasks: Includes Promise.then, MutationObserver, process.nextTick (Node.js)
Execution order follows:
// Typical execution order example
console.log('script start'); // Macrotask
setTimeout(() => {
console.log('setTimeout'); // Macrotask
}, 0);
Promise.resolve().then(() => {
console.log('promise1'); // Microtask
}).then(() => {
console.log('promise2'); // Microtask
});
console.log('script end'); // Macrotask
/*
Output order:
script start
script end
promise1
promise2
setTimeout
*/
Microtask Characteristics and Optimization
Microtasks execute immediately after the current macrotask ends, having higher priority. This feature can be used for:
Batch DOM Updates
// Inefficient approach
elements.forEach(el => {
el.style.width = '100px'; // Triggers multiple reflows
});
// Optimized solution
Promise.resolve().then(() => {
elements.forEach(el => {
el.style.width = '100px'; // Single reflow
});
});
State Synchronization
let data;
fetchData().then(res => {
data = res; // Assignment in microtask
});
// Subsequent code can be wrapped in a microtask to ensure data availability
Promise.resolve().then(() => {
processData(data);
});
Macrotask Optimization Strategies
Macrotasks are suitable for non-urgent time-consuming operations. Proper scheduling can prevent UI freezing:
Long Task Decomposition
// Original long task
function processHugeArray() {
for(let i=0; i<1e6; i++) {
// Time-consuming calculations...
}
}
// Decomposed into macrotasks
function chunkedProcess(array, index = 0, chunkSize = 1000) {
const end = Math.min(index + chunkSize, array.length);
for(; index < end; index++) {
// Process current chunk
}
if(index < array.length) {
setTimeout(() => chunkedProcess(array, index, chunkSize));
}
}
Animation Optimization
// Use requestAnimationFrame instead of setTimeout
function animate() {
// Animation logic...
requestAnimationFrame(animate);
}
animate();
Mixed Usage Scenarios
Complex interactions require coordination of both task types:
User Input Handling
input.addEventListener('input', () => {
// Immediate feedback (microtask)
Promise.resolve().then(updatePreview);
// Time-consuming operations (macrotask)
setTimeout(() => {
sendAnalytics();
updateDatabase();
}, 0);
});
Server-Side Data Stream Processing
function processStream(stream) {
stream.on('data', chunk => {
// Microtask for core data processing
Promise.resolve().then(() => parseChunk(chunk));
// Macrotask for non-critical operations
setTimeout(() => logChunk(chunk), 0);
});
}
Performance Pitfalls and Avoidance
Microtask Accumulation
// Dangerous recursive microtask
function recursiveMicrotask() {
Promise.resolve().then(recursiveMicrotask); // Causes main thread blockage
}
Macrotask Delay Accumulation
// Timer drift issue
let start = Date.now();
let count = 0;
function delayedTask() {
count++;
const offset = Date.now() - start;
console.log(`Execution ${count}, deviation: ${offset - count*100}ms`);
if(count < 10) {
setTimeout(delayedTask, 100);
}
}
setTimeout(delayedTask, 100);
Modern API Optimization Applications
queueMicrotask API
// More direct microtask API than Promise.resolve()
function criticalUpdate() {
queueMicrotask(() => {
updateUI();
logState();
});
}
requestIdleCallback
// Execute tasks during idle periods
requestIdleCallback((deadline) => {
while(deadline.timeRemaining() > 0) {
performBackgroundWork();
}
});
Framework Practices
React Batch Updates
// React uses microtasks for state batch updates
function Component() {
const [state, setState] = useState();
const handleClick = () => {
setState(1); // Doesn't immediately re-render
setState(2); // Merged update
};
}
Vue nextTick
// Vue's nextTick implementation
this.message = 'updated';
this.$nextTick(() => {
// Executes after DOM updates
console.log(this.$el.textContent);
});
Debugging and Monitoring
Chrome Performance Panel
- Identify long tasks (exceeding 50ms)
- Analyze task composition (macrotask/microtask ratio)
- Detect task scheduling frequency
PerformanceObserver
const observer = new PerformanceObserver((list) => {
for(const entry of list.getEntries()) {
console.log('[Long Task]', entry);
}
});
observer.observe({entryTypes: ['longtask']});
Node.js Environment Differences
process.nextTick vs setImmediate
// nextTick has higher priority than promise microtasks
process.nextTick(() => console.log(1));
Promise.resolve().then(() => console.log(2));
// Output order: 1, 2
// setImmediate belongs to macrotasks
setImmediate(() => console.log('Macrotask'));
File I/O Optimization
const fs = require('fs');
// Use setImmediate to decompose large file processing
function processLargeFile(path) {
fs.readFile(path, (err, data) => {
const chunks = splitData(data);
function processChunk(index) {
if(index >= chunks.length) return;
doWork(chunks[index]);
setImmediate(() => processChunk(index + 1));
}
processChunk(0);
});
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:垃圾回收机制理解与优化
下一篇:性能敏感代码的优化技巧