Введение: зачем нужны очереди задач в JavaScript?
В современной веб-разработке приложения часто сталкиваются с задачами, которые не могут быть выполнены мгновенно в рамках одного HTTP-запроса. Отправка электронных писем, обработка изображений, генерация PDF-отчетов или синхронизация с внешними API — все это требует времени и может "завесить" сервер, если выполнять их синхронно.
Очереди задач (Task Queues) решают эту проблему. Они позволяют отложить выполнение ресурсоемких операций и обрабатывать их асинхронно в фоновом режиме. В этой статье мы разберем, как создавать и управлять очередями задач в среде Node.js, используя популярные библиотеки.
1. Основы работы с очередями в Node.js
Очередь задач — это структура данных, работающая по принципу FIFO (First In, First Out). Задачи добавляются в конец очереди, а обрабатываются из начала. В контексте Node.js это означает, что ваш сервер может быстро ответить пользователю ("Задача принята в обработку"), а сама задача будет выполнена позже.
Для работы с очередями в Node.js используются специализированные библиотеки, которые полагаются на внешние хранилища (чаще всего Redis). Рассмотрим две самые популярные:
- Bull — современная, быстрая и стабильная библиотека на основе Redis.
- Kue — более старая, но всё ещё популярная библиотека с встроенным веб-интерфейсом.
Мы сосредоточимся на Bull, так как она является стандартом де-факто для новых проектов.
1.1 Установка и подключение Bull
Для начала установите необходимые зависимости:
npm install bull ioredis
Теперь создадим простую очередь:
const Queue = require('bull');
const myQueue = new Queue('emailQueue', {
redis: {
host: '127.0.0.1',
port: 6379
}
});
// Добавляем задачу в очередь
myQueue.add({
to: 'user@example.com',
subject: 'Приветственное письмо',
body: 'Добро пожаловать на наш сайт!'
});
console.log('Задача добавлена в очередь');
В этом примере мы создали очередь с именем emailQueue и добавили в нее одну задачу — отправку письма. Задача пока не выполнена, она просто ждет своего обработчика.
2. Обработка задач из очереди
Чтобы задачи начали выполняться, нам нужен обработчик (consumer). Он будет "слушать" очередь и брать из нее задачи для выполнения.
Создадим файл worker.js:
const Queue = require('bull');
const emailQueue = new Queue('emailQueue', {
redis: { host: '127.0.0.1', port: 6379 }
});
// Функция-обработчик
emailQueue.process(async (job) => {
const { to, subject, body } = job.data;
console.log(`Отправка письма на ${to}...`);
// Имитация отправки письма (в реальном проекте тут был бы nodemailer)
await new Promise(resolve => setTimeout(resolve, 2000));
console.log(`Письмо "${subject}" отправлено на ${to}`);
return { status: 'sent', email: to };
});
console.log('Обработчик запущен и ожидает задачи...');
Запустите этот скрипт, а затем добавьте задачу из первого примера. Вы увидите, как задача будет обработана.
2.1 Обработка ошибок и повторные попытки
В реальных проектах задачи могут завершаться ошибкой (например, SMTP-сервер недоступен). Bull предоставляет встроенный механизм повторных попыток:
emailQueue.add(
{ to: 'user@example.com', subject: 'Важное письмо' },
{
attempts: 5, // Количество попыток
backoff: { type: 'exponential', delay: 2000 } // Экспоненциальная задержка
}
);
// В обработчике можно обработать неудачу
emailQueue.on('failed', (job, err) => {
console.error(`Задача ${job.id} провалилась: ${err.message}`);
});
3. Расширенные возможности очередей
Bull предоставляет множество полезных функций для управления задачами.
3.1 Приоритеты задач
Вы можете задать приоритет (чем меньше число, тем выше приоритет):
// Высокий приоритет
myQueue.add({ type: 'critical' }, { priority: 1 });
// Низкий приоритет
myQueue.add({ type: 'bulk' }, { priority: 10 });
3.2 Задержка выполнения
Отложите выполнение задачи на определенное время:
// Отправить письмо через 1 час
myQueue.add(
{ to: 'user@example.com', subject: 'Напоминание' },
{ delay: 60 * 60 * 1000 } // 3600000 мс = 1 час
);
3.3 Повторяющиеся задачи (cron)
Настройте задачи, которые выполняются по расписанию:
const Queue = require('bull');
const reportQueue = new Queue('reportQueue');
// Генерация отчета каждый день в 9:00
reportQueue.add(
{ type: 'daily_report' },
{ repeat: { cron: '0 9 * * *' } }
);
reportQueue.process(async (job) => {
console.log('Генерирую ежедневный отчет...');
// ... логика генерации
});
4. Практический пример: система обработки заказов
Соберем всё вместе и создадим простую систему для обработки заказов в интернет-магазине.
Структура проекта:
orderQueue.js— создание очереди и добавление заказовorderWorker.js— обработчик, который выполняет несколько шагов
orderQueue.js:
const Queue = require('bull');
const orderQueue = new Queue('orderProcessing', {
redis: { host: '127.0.0.1', port: 6379 }
});
// Добавляем новый заказ
async function addOrder(orderData) {
const job = await orderQueue.add(orderData, {
attempts: 3,
backoff: { type: 'fixed', delay: 5000 }
});
console.log(`Заказ #${job.id} добавлен в очередь`);
return job.id;
}
// Пример добавления
addOrder({
orderId: 12345,
userId: 42,
items: ['Ноутбук', 'Мышь'],
total: 1500
});
orderWorker.js:
const Queue =