Ответ
Node.js использует однопоточный цикл событий (event loop) на базе libuv для неблокирующей обработки операций ввода-вывода (I/O). Когда встречается асинхронная операция (например, запрос к БД или чтение файла), она передается в системный пул потоков libuv, а основной поток Node.js продолжает выполнять синхронный код. По завершении асинхронной операции её колбэк помещается в соответствующую очередь (например, очередь микрозадач для Promise или очередь макрозадач для таймеров и I/O), и цикл событий выполняет его на следующей итерации, когда стек вызовов пуст.
Ключевые компоненты:
- Event Loop: Однопоточный цикл, который координирует выполнение колбэков.
- Libuv: Библиотека на C++, предоставляющая пул потоков для выполнения блокирующих системных операций.
- Очереди: Разные приоритетные очереди для микрозадач (nextTick, Promises) и макрозадач (setTimeout, I/O).
Пример с async/await (современный подход):
const fs = require('fs').promises;
async function readFileAsync() {
console.log('Начинаем чтение...');
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log('Содержимое файла:', data);
} catch (err) {
console.error('Ошибка чтения:', err);
}
console.log('Этот лог выполнится после await');
}
readFileAsync();
console.log('Этот лог выполнится ДО завершения чтения файла');
Почему так важно: Эта модель позволяет Node.js эффективно обрабатывать тысячи одновременных соединений с минимальными накладными расходами на потоки, что идеально подходит для I/O-интенсивных приложений, таких как веб-серверы или API.