Node.js Timers 模块
Node.js 的 timers
模块提供了一系列函数,用于在特定时间后执行代码或在指定时间间隔重复执行代码。这些功能对于实现延迟执行、周期性任务和超时控制非常有用。
虽然 timers
模块是 Node.js 的核心模块,但它的方法如 setTimeout()
, setInterval()
和 setImmediate()
是全局可用的,无需显式导入。
主要定时器函数
setTimeout()
setTimeout()
函数用于在指定的毫秒数后执行一次回调函数。
实例
// 2秒后打印消息
setTimeout(() => {
console.log('这条消息将在2秒后显示');
}, 2000);
setTimeout(() => {
console.log('这条消息将在2秒后显示');
}, 2000);
参数说明
- 回调函数:要执行的函数
- 延迟时间:以毫秒为单位的时间(1000毫秒=1秒)
- 可选参数:传递给回调函数的额外参数
实例
// 带额外参数的示例
setTimeout((name, age) => {
console.log(`你好,${name},你今年${age}岁`);
}, 1000, '张三', 25);
setTimeout((name, age) => {
console.log(`你好,${name},你今年${age}岁`);
}, 1000, '张三', 25);
clearTimeout()
clearTimeout()
用于取消之前通过 setTimeout()
设置的定时器。
实例
const timerId = setTimeout(() => {
console.log('这条消息不会显示');
}, 2000);
clearTimeout(timerId); // 取消定时器
console.log('这条消息不会显示');
}, 2000);
clearTimeout(timerId); // 取消定时器
setInterval()
setInterval()
函数用于每隔指定的毫秒数重复执行回调函数。
实例
// 每隔1秒打印一次消息
let counter = 0;
const intervalId = setInterval(() => {
counter++;
console.log(`这是第 ${counter} 次执行`);
if (counter === 5) {
clearInterval(intervalId); // 停止定时器
}
}, 1000);
let counter = 0;
const intervalId = setInterval(() => {
counter++;
console.log(`这是第 ${counter} 次执行`);
if (counter === 5) {
clearInterval(intervalId); // 停止定时器
}
}, 1000);
clearInterval()
clearInterval()
用于停止之前通过 setInterval()
设置的重复定时器。
实例
const intervalId = setInterval(() => {
console.log('这条消息会重复显示');
}, 1000);
// 5秒后停止定时器
setTimeout(() => {
clearInterval(intervalId);
}, 5000);
console.log('这条消息会重复显示');
}, 1000);
// 5秒后停止定时器
setTimeout(() => {
clearInterval(intervalId);
}, 5000);
setImmediate()
setImmediate()
函数用于在当前事件循环结束时执行回调函数。
实例
console.log('开始');
setImmediate(() => {
console.log('立即执行回调');
});
console.log('结束');
// 输出顺序:
// 开始
// 结束
// 立即执行回调
setImmediate(() => {
console.log('立即执行回调');
});
console.log('结束');
// 输出顺序:
// 开始
// 结束
// 立即执行回调
clearImmediate()
clearImmediate()
用于取消之前通过 setImmediate()
设置的立即执行回调。
实例
const immediateId = setImmediate(() => {
console.log('这条消息不会显示');
});
clearImmediate(immediateId);
console.log('这条消息不会显示');
});
clearImmediate(immediateId);
定时器的执行顺序
理解 Node.js 事件循环对于掌握定时器的执行顺序非常重要。以下是几种定时器的执行顺序:
- 同步代码:最先执行
- process.nextTick():在当前阶段结束后立即执行
- Promise 回调:微任务队列
- setImmediate():在 I/O 回调之后执行
- setTimeout() 和 setInterval():在检查阶段执行
实例
console.log('开始');
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('Promise'));
console.log('结束');
// 典型输出顺序:
// 开始
// 结束
// nextTick
// Promise
// setTimeout
// setImmediate
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('Promise'));
console.log('结束');
// 典型输出顺序:
// 开始
// 结束
// nextTick
// Promise
// setTimeout
// setImmediate
高级用法与注意事项
定时器的取消
所有定时器函数都会返回一个 Timeout
对象,可以用于取消定时器。即使定时器已经执行,调用取消函数也是安全的。
实例
const timeout = setTimeout(() => {}, 1000);
clearTimeout(timeout); // 安全调用,即使定时器已执行
clearTimeout(timeout); // 安全调用,即使定时器已执行
定时器的精度
Node.js 的定时器不能保证精确的执行时间,它们表示的是"至少"等待的时间。系统负载和其他因素可能会影响实际执行时间。
内存泄漏
忘记清除 setInterval()
定时器是常见的内存泄漏来源。确保在不需要时清除定时器。
实例
// 错误的做法 - 可能导致内存泄漏
setInterval(() => {
// 执行某些操作
}, 1000);
// 正确的做法 - 在适当的时候清除定时器
const interval = setInterval(() => {
// 执行某些操作
if (someCondition) {
clearInterval(interval);
}
}, 1000);
setInterval(() => {
// 执行某些操作
}, 1000);
// 正确的做法 - 在适当的时候清除定时器
const interval = setInterval(() => {
// 执行某些操作
if (someCondition) {
clearInterval(interval);
}
}, 1000);
递归 setTimeout 替代 setInterval
对于需要固定间隔但每次执行时间不确定的任务,使用递归 setTimeout
比 setInterval
更可靠。
实例
function doSomething() {
console.log('执行任务...');
// 模拟任务执行时间不确定
const delay = Math.random() * 1000;
setTimeout(doSomething, 1000 + delay);
}
setTimeout(doSomething, 1000);
console.log('执行任务...');
// 模拟任务执行时间不确定
const delay = Math.random() * 1000;
setTimeout(doSomething, 1000 + delay);
}
setTimeout(doSomething, 1000);
实际应用示例
实现简单的倒计时
实例
function countdown(seconds) {
let current = seconds;
console.log(`倒计时开始:${current}秒`);
const timer = setInterval(() => {
current--;
if (current <= 0) {
clearInterval(timer);
console.log('倒计时结束!');
} else {
console.log(`剩余时间:${current}秒`);
}
}, 1000);
}
countdown(5);
let current = seconds;
console.log(`倒计时开始:${current}秒`);
const timer = setInterval(() => {
current--;
if (current <= 0) {
clearInterval(timer);
console.log('倒计时结束!');
} else {
console.log(`剩余时间:${current}秒`);
}
}, 1000);
}
countdown(5);
请求超时处理
实例
function fetchWithTimeout(url, timeout = 5000) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(`请求超时(${timeout}ms)`));
}, timeout);
fetch(url)
.then(response => {
clearTimeout(timeoutId);
resolve(response);
})
.catch(error => {
clearTimeout(timeoutId);
reject(error);
});
});
}
// 使用示例
fetchWithTimeout('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error(error));
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(`请求超时(${timeout}ms)`));
}, timeout);
fetch(url)
.then(response => {
clearTimeout(timeoutId);
resolve(response);
})
.catch(error => {
clearTimeout(timeoutId);
reject(error);
});
});
}
// 使用示例
fetchWithTimeout('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error(error));
通过掌握 Node.js 的 timers
模块,你可以有效地控制代码的执行时机,实现各种定时任务和异步控制流程。
点我分享笔记