Spec-Zone.ru › Web APIs

Использование событий, отправляемых сервером

Базовая поддержка Широко доступно *

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна во всех браузерах с января 2020 года.

* Некоторые части этой функции могут иметь различный уровень поддержки.

  • Узнать больше
  • Полная совместимость
  • Отправить отзыв

Разработка веб-приложения, использующего события, отправляемые сервером, проста. Вам понадобится немного кода на стороне сервера для потоковой передачи событий на фронтенд, но код на стороне клиента работает почти идентично коду веб-сокеттов в части обработки входящих событий. Это одностороннее соединение, поэтому вы не можете отправлять события с клиента на сервер.

Получение событий с сервера

API событий, отправляемых сервером, содержится в интерфейсе EventSource.

Создание экземпляра EventSource

Чтобы открыть соединение с сервером для начала получения событий с него, создайте новый объект EventSource с URL-адресом скрипта, который генерирует события. Например:

const evtSource = new EventSource("sse-demo.php");

Если скрипт генератора событий размещен на другом происхождении, новый объект EventSource должен быть создан с URL-адресом и словарем опций. Например, предполагая, что скрипт клиента находится на example.com:

const evtSource = new EventSource("//api.example.com/sse-demo.php", {
  withCredentials: true,
});

Прослушивание событий message

Сообщения, отправленные с сервера, которые не имеют поля event, принимаются как события message. Для получения событий сообщений подключите обработчик события message:

evtSource.onmessage = (event) => {
  const newElement = document.createElement("li");
  const eventList = document.getElementById("list");

  newElement.textContent = `message: ${event.data}`;
  eventList.appendChild(newElement);
};

Этот код прослушивает входящие события сообщений и добавляет текст сообщения в список в HTML-документе.

Прослушивание пользовательских событий

Сообщения с сервера, у которых определено поле event, принимаются как события с именем, указанным в event. Например:

evtSource.addEventListener("ping", (event) => {
  const newElement = document.createElement("li");
  const eventList = document.getElementById("list");
  const time = JSON.parse(event.data).time;
  newElement.textContent = `ping at ${time}`;
  eventList.appendChild(newElement);
});

Этот код будет вызываться всякий раз, когда сервер отправляет сообщение с полем event, установленным в ping; затем он анализирует JSON в поле data и выводит эту информацию.

Предупреждение: При использовании не по HTTP/2 SSE ограничен максимальным числом открытых подключений, что особенно неприятно при открытии нескольких вкладок, поскольку ограничение действует на браузер и установлено на очень низкое значение (6). Эта проблема помечена как «Не будем исправлять» в Chrome и Firefox. Это ограничение относится к одному браузеру + домену, что означает, что вы можете открыть 6 подключений SSE через все вкладки к www.example1.com и еще 6 подключений SSE к www.example2.com (согласно Stack Overflow). При использовании HTTP/2 максимальное количество одновременных HTTP-потоков согласовывается между сервером и клиентом (по умолчанию 100).

Отправка событий с сервера

Скрипт на стороне сервера, отправляющий события, должен отвечать с помощью типа MIME text/event-stream. Каждое уведомление отправляется как блок текста, завершённый парой символов новой строки. Подробнее о формате потока событий см. в разделе Формат потока событий.

Следующий код на PHP для примера, который мы используем:

date_default_timezone_set("America/New_York");
header("X-Accel-Buffering: no");
header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");

$counter = rand(1, 10);
while (true) {
  // Every second, send a "ping" event.

  echo "event: ping\n";
  $curDate = date(DATE_ISO8601);
  echo 'data: {"time": "' . $curDate . '"}';
  echo "\n\n";

  // Send a simple message at random intervals.

  $counter--;

  if (!$counter) {
    echo 'data: This is a message at time ' . $curDate . "\n\n";
    $counter = rand(1, 10);
  }

  if (ob_get_contents()) {
      ob_end_flush();
  }
  flush();

  // Break the loop if the client aborted the connection (closed the page)

  if (connection_aborted()) break;

  sleep(1);
}

Приведённый выше код генерирует событие каждую секунду с типом события "ping". Данные каждого события — это JSON-объект, содержащий временную метку в формате ISO 8601, соответствующую времени генерации события. В случайные моменты времени отправляется простое сообщение (без типа события). Цикл будет продолжаться независимо от состояния соединения, поэтому включена проверка на прерывание цикла, если соединение закрыто (например, клиент закрывает страницу).

Примечание: Полный пример с использованием показанного в этой статье кода можно найти на GitHub — см. Демонстрация простого SSE с использованием PHP.

Обработка ошибок

При возникновении проблем (таких как тайм-аут сети или проблемы с контролем доступа) генерируется событие об ошибке. Вы можете выполнить действия по этому поводу программно, реализовав обратный вызов onerror для объекта EventSource:

evtSource.onerror = (err) => {
  console.error("EventSource failed:", err);
};

Закрытие потоков событий

По умолчанию, если соединение между клиентом и сервером закрывается, соединение перезапускается. Соединение завершается методом .close().

evtSource.close();

Формат потока событий

Поток событий — это простой поток текстовых данных, который должен быть закодирован с использованием UTF-8. Сообщения в потоке событий разделяются парой символов новой строки. Двоеточие в качестве первого символа строки по сути является комментарием и игнорируется.

Примечание: Строка комментария может использоваться для предотвращения истечения срока действия соединений; сервер может периодически отправлять комментарий, чтобы поддерживать соединение.

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

Поля

В каждом полученном сообщении есть некоторая комбинация следующих полей, по одному на строке:

event

Строка, определяющая тип события. Если это указано, в браузере будет отправлено событие слушателю, соответствующему указанному имени события; код веб-сайта должен использовать addEventListener() для прослушивания событий с именами. Обработчик onmessage вызывается, если для сообщения не указано имя события.

data

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

id

Идентификатор события для установки значения последнего идентификатора события объекта EventSource.

retry

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

Все остальные имена полей игнорируются.

Примечание: Если строка не содержит двоеточия, вся строка обрабатывается как имя поля со строкой-значением пустой строки.

Примеры

Только сообщения с данными

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

Второе сообщение содержит поле данных со значением "some text". Третье сообщение содержит поле данных со значением "another message\nwith two lines". Обратите внимание на символ новой строки в значении.

: this is a test stream

data: some text

data: another message
data: with two lines

Именованные события

Этот пример отправляет именованные события. У каждого есть имя события, указанное в поле event и поле data со значением соответствующей JSON-строки с данными, необходимыми клиенту для реагирования на событие. Поле data может, конечно, содержать любые данные строки; это не обязательно должен быть JSON.

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}

event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}

event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}

Смешанные сообщения

Вы можете использовать не только сообщения без имен или типизированные события; вы можете смешивать их в одном потоке событий.

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

data: Here's a system message of some kind that will get used
data: to accomplish some task.

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}

Совместимость с браузерами

Рабочий стол Мобильный
Chrome Edge Firefox Opera Safari Chrome Android Firefox для Android Opera Android Safari на IOS Samsung Internet WebView Android
EventSource 6 79 6 11 5 18 45 12 5 1.0 4.4
Using_server-sent_events 6 79 6 11 5 18 45 11 5 1.0 4.4
close 6 79 6 12 5 18 45 12 5 1.0 4.4
error_event 6 79 6 12 5 18 45 12 5 1.0 4.4
message_event 6 79 6 12 5 18 45 12 5 1.0 4.4
open_event 6 79 6 12 5 18 45 12 5 1.0 4.4
readyState 6 79 6 12 5 18 45 12 5 1.0 4.4
url 18 79 6 12 6 18 45 12 6 1.0 4.4
withCredentials 26 79 6 12 7 26 45 12 7 1.5 4.4
worker_support 6 79 133
53–133Не поддерживается в службах рабочего стола.
≤15 5 18 133
53–133Не поддерживается в службах рабочего стола.
≤14 5 1.0 ≤37

© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API