Gernar
Frontend DeveloperHTTP, API и сеть

Интервью-вопрос

Что такое Long Polling

Long Polling помогает получать почти realtime-обновления через обычные HTTP-запросы. На интервью важно объяснить не только принцип, но и клиентский цикл. Расскажите про таймауты, retry, отмену запроса и защиту от потери событий.

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 60 правильно

Как лучше коротко объяснить Long Polling на интервью?

Вы хотите дать определение без длинной лекции.

Варианты ответа

Разбор

Разобраться, а не зазубрить

Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.

Базовая идея

Long Polling помогает в ситуации, когда серверу нужно сообщить клиенту о новом событии, но постоянное WebSocket-соединение не используется. Клиент делает обычный HTTP-запрос, например на endpoint уведомлений. Если новых событий нет, сервер держит запрос открытым.

Когда событие появляется, сервер отвечает данными. Если событие не появилось за заданное время, сервер завершает запрос по таймауту, часто пустым ответом. После любого штатного завершения клиент начинает следующий запрос.

Короткая формулировка для интервью:

Long Polling это цикл HTTP-запросов, где сервер отвечает не сразу, а когда появились данные или истек таймаут. Клиент после ответа запускает следующий запрос и получает обновления почти в реальном времени.

Чем это отличается от обычного polling

При обычном polling клиент ходит на сервер по расписанию. Например, раз в секунду, раз в пять секунд или раз в минуту. Сервер отвечает сразу, даже если данных нет. Это просто, но часто дает много пустых запросов и лишнюю задержку. Событие может появиться сразу после проверки, а клиент узнает о нем только на следующем тике.

При Long Polling запрос может висеть до события. Если событие появилось через 200 мс, клиент получит его почти сразу. Если ничего не произошло, запрос закроется по таймауту и начнется новый цикл.

Практический вывод простой. Long Polling уменьшает число пустых ответов и среднюю задержку для редких событий, но не убирает стоимость HTTP-запросов полностью.

Как выбрать транспорт

На интервью сильный ответ не заканчивается фразой, что это альтернатива WebSocket. Лучше сразу показать границы выбора. Long Polling подходит не потому, что он современнее. Он полезен, когда HTTP-цепочка проще проходит через инфраструктуру и достаточно хороша для редких событий.

Как выбрать подход для realtime

1Нужны редкие обновления и простая совместимость с HTTP-инфраструктурой?
Long Polling может быть нормальным выбором или fallback.
2Нужен постоянный двусторонний обмен сообщениями?
Смотрите в сторону WebSocket. Long Polling станет дорогим и шумным.
3Сервер только пушит события клиенту, а клиент почти не отвечает?
Проверьте SSE. Он часто проще для однонаправленного потока.
4Обновления не срочные и допустима задержка в несколько секунд?
Обычный polling может быть проще и дешевле в сопровождении.

Клиентский цикл во фронтенде

Во фронтенде Long Polling опасен не самим fetch, а бесконечным циклом вокруг него. Важно не запустить несколько циклов случайно. Еще важно не потерять событие между запросами и не продолжать работу после ухода пользователя со страницы.

Пример ниже показывает общий подход. Это не универсальная библиотека, а схема. Один активный запрос, курсор, отмена через AbortController и пауза при ошибке. В URL передается только несекретный cursor, а не токен доступа.

import { useEffect } from "react";

function useNotificationsPolling() {
  useEffect(() => {
    const controller = new AbortController();
    let stopped = false;
    let cursor: string | null = null;

    async function loop() {
      while (!stopped) {
        try {
          const url = new URL("/api/notifications/long-poll", window.location.origin);

          if (cursor) {
            url.searchParams.set("cursor", cursor);
          }

          const response = await fetch(url, { signal: controller.signal });

          if (response.status === 204) {
            continue;
          }

          if (response.status === 401 || response.status === 403) {
            // requestLogin();
            return;
          }

          if (!response.ok) {
            throw new Error(`Request failed: ${response.status}`);
          }

          const payload = await response.json();

          cursor = payload.cursor;
          // updateNotifications(payload.items);
        } catch (error) {
          if (controller.signal.aborted) {
            return;
          }

          await new Promise((resolve) => setTimeout(resolve, 1000));
        }
      }
    }

    loop();

    return () => {
      stopped = true;
      controller.abort();
    };
  }, []);
}

В реальном проекте retry лучше делать с backoff и jitter. Ошибки авторизации стоит обрабатывать отдельно. Если токен истек, бесконечно повторять запрос бессмысленно. Нужно обновить сессию или вернуть пользователя к логину. Данные из ответа нельзя вставлять в HTML без очистки. Уведомления и сообщения приходят с сервера, поэтому опасная разметка может стать XSS.

Безопасный цикл
  1. 1Передать cursor последнего события
  2. 2Дождаться данных или таймаута
  3. 3Обновить UI и сохранить новый cursor
  4. 4Запустить следующий запрос
  5. 5Отменить запрос при уходе со страницы
Опасный цикл
  1. 1Запустить несколько запросов параллельно
  2. 2Игнорировать timeout и сетевые ошибки
  3. 3Не хранить id последнего события
  4. 4Повторять запросы без backoff
  5. 5Оставить запрос жить после unmount

Таймауты, ошибки и порядок событий

Таймаут без данных это штатный результат. Сервер не обязан держать запрос бесконечно. Есть лимиты прокси, балансировщика, приложения и браузера. Клиент должен спокойно начать следующий запрос.

Сетевые ошибки и ответы 5xx лучше считать временной проблемой. Но не повторяйте запрос мгновенно без конца. Иначе при деградации сервера тысячи клиентов начнут стучаться еще чаще.

Для порядка событий нужен контракт приложения. Обычно клиент отправляет lastEventId или cursor, а сервер возвращает события после этой точки. Тогда клиент может пережить короткий обрыв, перезагрузку вкладки или дубль ответа без потери данных. Обработчик на клиенте должен быть идемпотентным. Если событие пришло повторно, UI не должен показать второе одинаковое уведомление.

Что сказать про серверную сторону

Frontend Developer не обязан проектировать сервер целиком, но на интервью полезно показать понимание цены решения. Long Polling держит открытые соединения. Значит сервер, прокси и балансировщик должны быть к этому готовы.

Можно сказать так:

Я понимаю, что Long Polling выглядит просто на клиенте, но на сервере нужны корректные таймауты, лимиты, освобождение ресурсов и поддержка long-lived запросов в прокси. Иначе решение будет работать в демо, но начнет ломаться под нагрузкой.

Такой ответ звучит сильнее, чем простое перечисление WebSocket и SSE. Вы показываете, что думаете о продакшен-рисках.

Практический вывод

Если вас спрашивают, что такое Long Polling, начните с механики одного запроса. Потом сразу добавьте цикл. Ответ получили, запускаете следующий запрос. После этого покажите разницу с polling и границы выбора рядом с WebSocket и SSE.

Хороший короткий ответ занимает 4-6 предложений. Не уходите в историю realtime-технологий. Достаточно объяснить поведение, последствия для UI и ошибки, которые вы бы обработали в коде.

Частые ошибки

Где обычно ошибаются

Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.

  1. 1

    Путать Long Polling с обычным polling

    При polling клиент сам решает, когда спросить сервер, и часто получает пустой ответ. При Long Polling сервер ждет событие и отвечает позже. Поэтому вам нужно иначе обработать таймауты, сетевые ошибки и повторный запрос.

  2. 2

    Не отменять запрос при unmount

    Если компонент ушел со страницы, а запрос продолжает жить, ответ может прийти в уже неактуальное состояние. Используйте AbortController или cleanup в эффекте. Так вы избежите утечек, лишних запросов и обновления старого UI.

  3. 3

    Делать мгновенный retry при любой ошибке

    После сетевого сбоя или ответа 5xx мгновенные повторы из многих вкладок могут усилить проблему на сервере. Лучше добавить backoff, jitter и отдельную обработку авторизационных ошибок.

  4. 4

    Не передавать курсор события

    Если клиент просто спрашивает, есть ли что-то новое, он может потерять событие между двумя запросами или получить дубль. Надежнее передавать lastEventId, cursor или похожий маркер и делать обработку идемпотентной.

  5. 5

    Класть секреты в URL запроса

    У long polling URL часто есть параметр cursor, но это не место для токена доступа. Query string может попасть в серверные логи, историю браузера, аналитику и заголовок Referer. Безопаснее передавать авторизацию так же, как в остальных API-запросах проекта, и не смешивать секреты с курсором события.

  6. 6

    Выбирать Long Polling для очень частых сообщений

    При частых событиях каждый ответ закрывает HTTP-запрос, а клиент тут же открывает новый. Это дает лишние заголовки, нагрузку на прокси и задержки. Для таких сценариев обычно лучше WebSocket.

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми проверяют понимание Long Polling, таймаутов и выбора realtime-транспорта.

Живые ответы

Видео с похожим вопросом

Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.

Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.

Содержание