Як працюють таймери та планування в Node.js?
Таймери в Node.js
Node.js надає кілька функцій таймерів для планування виконання коду. Вони інтегруються з циклом подій і виконуються на різних етапах.
Функції таймерів
setTimeout — Виконати один раз після затримки
js
const timer = setTimeout(() => {
console.log('Виконано через 2 секунди');
}, 2000);
// Скасувати за необхідності
clearTimeout(timer);setInterval — Виконувати повторно
js
let count = 0;
const interval = setInterval(() => {
console.log(`Тік ${++count}`);
if (count >= 5) clearInterval(interval);
}, 1000);setImmediate — Виконати на наступній ітерації циклу подій
js
setImmediate(() => {
console.log('Виконується на етапі перевірки циклу подій');
});process.nextTick — Виконати перед будь-яким I/O
js
process.nextTick(() => {
console.log('Виконується перед будь-яким I/O в мікрозавданні');
});Порядок виконання
js
console.log('1 - синхронний');
setTimeout(() => console.log('2 - setTimeout'), 0);
setImmediate(() => console.log('3 - setImmediate'));
process.nextTick(() => console.log('4 - nextTick'));
Promise.resolve().then(() => console.log('5 - Promise'));
console.log('6 - синхронний');
// Вихід:
// 1 - синхронний
// 6 - синхронний
// 4 - nextTick
// 5 - Promise
// 2 - setTimeout (або 3 — порядок між setTimeout(0) і setImmediate
// 3 - setImmediate // є недетермінованим поза циклом I/O)Точність таймерів
Таймери в Node.js є не точними. Вони гарантують мінімальну затримку, а не точну:
js
const start = Date.now();
setTimeout(() => {
console.log(`Фактична затримка: ${Date.now() - start}ms`);
// Буде >= 100ms, часто 101-105ms
}, 100);Розширене: AbortController з таймерами
js
const { setTimeout: sleep } = require('timers/promises');
async function fetchWithTimeout(url, ms) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), ms);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
return response;
} catch (err) {
if (err.name === 'AbortError') {
throw new Error(`Запит перевищив час очікування після ${ms}ms`);
}
throw err;
}
}Промісовані таймери (Node.js 16+)
js
const { setTimeout, setInterval, setImmediate } = require('timers/promises');
// Очікування затримки
await setTimeout(1000);
console.log('Пройшла 1 секунда');
// Ітерація за інтервалом
const interval = setInterval(1000);
for await (const _ of interval) {
console.log('тік');
break; // зупинити після першого тіку
}Загальні патерни
Дебаунс
js
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}Повтор з експоненційною затримкою
js
async function retryWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (err) {
if (i === maxRetries - 1) throw err;
const delay = Math.pow(2, i) * 1000; // 1с, 2с, 4с
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}Підсумкова таблиця
| Функція | Коли виконується | Етап |
|---|---|---|
process.nextTick() | Після поточної операції, перед I/O | Мікрозавдання |
Promise.then() | Після nextTick | Мікрозавдання |
setTimeout(fn, 0) | Мінімальна затримка після поточного циклу | Таймери |
setImmediate() | Наступна ітерація, етап перевірки | Перевірка |
setInterval() | Повторно через інтервал | Таймери |
Найкраща практика: Використовуйте
setImmediate()для розподілу роботи, що вимагає великих обчислень. ВикористовуйтеsetTimeout()для фактичних затримок. Віддавайте перевагуtimers/promisesдля async/await. УникайтеsetIntervalу серверному коді — використовуйте бібліотеки cron або черги повідомлень замість цього.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.