面试题答案
一键面试优化策略
- 使用
setImmediate
替代setTimeout
:setTimeout
会在指定时间后将回调放入事件循环的宏任务队列。而setImmediate
会在当前轮询阶段结束时,将回调放入事件循环的check
阶段队列。在 I/O 操作后,setImmediate
总是比setTimeout
先执行,这样可以更快地处理一些不需要立即执行但需要尽快执行的任务,减少事件循环的阻塞。- 适用场景:适用于处理一些在当前 I/O 操作完成后需要尽快执行,但又不希望阻塞当前事件循环的任务,比如日志记录、统计等辅助性任务。
- 合理设置定时器间隔:
- 在高并发场景下,如果定时器间隔设置得过小,会导致大量定时器任务频繁触发,加重事件循环负担。应根据任务的实际需求,合理设置定时器间隔,避免不必要的频繁执行。
- 适用场景:对于一些周期性更新数据的任务,如缓存更新、监控数据采集等,要根据数据变化频率和业务需求,合理设置定时器间隔。
- 批量处理定时器任务:
- 可以将多个相关的定时器任务合并为一个任务进行处理。例如,多个需要定期更新不同模块数据的任务,可以合并为一个任务,在这个任务中依次处理各个模块的数据更新,减少定时器的数量。
- 适用场景:当多个定时器任务具有相似的功能或者处理的数据有相关性时,适合批量处理。比如在一个电商系统中,定期更新商品库存、销量等多个相关数据的任务可以合并。
- 使用
process.nextTick
:process.nextTick
会将回调放入nextTickQueue
,这个队列会在当前操作完成后,下一次事件循环的poll
阶段之前执行。它的优先级比setImmediate
更高,适用于一些需要在当前操作完成后立即执行,但又不想阻塞事件循环的任务。- 适用场景:适用于一些需要在函数调用栈清空后立即执行的任务,比如在模块初始化后立即执行一些配置操作。
实际案例及代码实现
- 使用
setImmediate
替代setTimeout
:
// 使用 setTimeout
setTimeout(() => {
console.log('setTimeout callback');
}, 0);
// 使用 setImmediate
setImmediate(() => {
console.log('setImmediate callback');
});
在上述代码中,如果在 I/O 操作之后执行这段代码,setImmediate
的回调会先于 setTimeout
的回调执行。
2. 合理设置定时器间隔:
// 假设我们要定期获取系统负载
// 不合理设置(间隔过短)
// setInterval(() => {
// const loadavg = require('os').loadavg();
// console.log('Load average:', loadavg);
// }, 100);
// 合理设置(间隔适当,比如每分钟获取一次)
setInterval(() => {
const loadavg = require('os').loadavg();
console.log('Load average:', loadavg);
}, 60 * 1000);
- 批量处理定时器任务:
// 假设我们有两个模块,需要定期更新数据
// 模块 1
function updateModule1() {
console.log('Updating module 1');
}
// 模块 2
function updateModule2() {
console.log('Updating module 2');
}
// 分开设置定时器
// setInterval(updateModule1, 5000);
// setInterval(updateModule2, 5000);
// 合并为一个定时器任务
setInterval(() => {
updateModule1();
updateModule2();
}, 5000);
- 使用
process.nextTick
:
function setup() {
let config = {};
// 初始化一些配置
config.host = 'localhost';
config.port = 3000;
process.nextTick(() => {
console.log('Using config:', config);
});
}
setup();
在上述代码中,process.nextTick
的回调会在 setup
函数调用栈清空后立即执行,打印出配置信息。