Long Polling
Long Polling — техника эмуляции push-уведомлений через HTTP: клиент отправляет запрос и сервер держит соединение открытым до появления новых данных.
Зачем нужно
Настоящий WebSocket требует специальной поддержки на сервере и инфраструктуре. Long Polling работает поверх обычного HTTP и совместим с любым прокси и CDN. Это простейший способ получить «почти реальное время» без смены протокола.
Где используется
- Чат-приложения до появления WebSocket (раннее поколение)
- Системы уведомлений, где WebSocket избыточен
- Среды с ограниченной инфраструктурой (нет WebSocket-поддержки на прокси)
- Fallback-транспорт в библиотеках (Socket.io использует long polling как запасной)
Сравнение техник real-time
| Техника | Направление | Протокол | Когда выбирать |
|---|---|---|---|
| Short Polling | клиент→сервер (интервал) | HTTP | Редкие обновления, просто |
| Long Polling | клиент→сервер (удержание) | HTTP | Нет WS-поддержки |
| SSE | сервер→клиент | HTTP | Только push, нет диалога |
| WebSocket | двустороннее | WS | Чат, игры, real-time |
Реализация
Клиент (JavaScript)
async function longPoll(url, onMessage, signal) {
while (!signal?.aborted) {
try {
const res = await fetch(url, {
signal,
// Сервер ответит только при появлении данных (или по таймауту)
});
if (res.ok) {
const data = await res.json();
onMessage(data);
}
} catch (err) {
if (err.name === 'AbortError') break;
// При сетевой ошибке — небольшая пауза перед повтором
await new Promise(r => setTimeout(r, 1000));
}
// Сразу снова отправляем запрос
}
}
// Использование
const controller = new AbortController();
longPoll('/api/notifications', (data) => {
console.log('Новое уведомление:', data);
}, controller.signal);
// Остановить при уходе со страницы
window.addEventListener('beforeunload', () => controller.abort());
Сервер (Node.js / Express)
const pending = new Map(); // userId → res
app.get('/api/notifications', (req, res) => {
const userId = req.user.id;
// Сохраняем объект ответа — не отвечаем сразу
pending.set(userId, res);
// Таймаут: если нет данных за 30 сек — возвращаем пустой ответ
const timer = setTimeout(() => {
pending.delete(userId);
res.json({ type: 'timeout' });
}, 30_000);
// Очистка при закрытии соединения клиентом
req.on('close', () => {
clearTimeout(timer);
pending.delete(userId);
});
});
// Когда появилось уведомление — отвечаем ожидающему клиенту
function pushNotification(userId, notification) {
const res = pending.get(userId);
if (res) {
pending.delete(userId);
res.json({ type: 'notification', data: notification });
}
}
Частые ошибки
- Не ставят таймаут на сервере — соединения «зависают» при уходе клиента
- Не обрабатывают событие
closeзапроса — утечка памяти (res остаётся в Map) - Не делают паузу при ошибке на клиенте — бесконечный цикл запросов при недоступном сервере
- Используют long polling для высокочастотных событий — лучше WebSocket или SSE
Связанные темы
- _MOC Сеть
- Server-Sent Events (SSE)
- Socket.io -- обзор
- Протокол HTTP -- основы
- Abort Controller -- отмена запросов