Выполнение Ajax-вызовов с использованием объекта XmlHttpRequest является хорошо отработанным методом генерации динамических запросов к серверу. Однако Ajax не позволяет серверу напрямую инициировать передачу данных клиенту — метод, называемый push-технологией . Вот тут -то и появляется API событий, отправленных сервером . Специализируясь на технологии push, отправленные сервером события передают данные клиентам в виде непрерывного потока, называемого потоком событий , по соединению, которое остается открытым. И, поддерживая открытое соединение, устраняются накладные расходы на повторное установление нового соединения.
Сравнение с WebSockets
Многие люди совершенно не знают о существовании событий, отправленных сервером. Это потому, что их часто затмевает более мощный API WebSockets . В то время как WebSockets разрешают двунаправленную полнодуплексную связь между клиентом и сервером, отправленные сервером события позволяют отправлять клиенту сообщения только с сервера. Приложения, которые требуют производительности почти в реальном времени или двусторонней связи, вероятно, лучше подходят для WebSockets.
Однако отправленные сервером события также имеют определенные преимущества перед WebSockets. Например, отправленные сервером события поддерживают настраиваемые типы сообщений и автоматическое переподключение для сброшенных соединений. Эти функции могут быть реализованы в WebSockets, но они доступны по умолчанию для событий, отправляемых сервером. Приложениям WebSockets также требуются серверы, поддерживающие протокол WebSockets. Для сравнения, отправленные сервером события строятся поверх HTTP и могут быть реализованы на стандартных веб-серверах.
Обнаружение поддержки
Отправленные сервером события относительно хорошо поддерживаются, и Internet Explorer является единственным основным браузером, который еще не поддерживает их . Однако, пока IE отстает, по-прежнему необходимо обеспечить обнаружение функций. На стороне клиента отправляемые сервером события реализуются с помощью объекта EventSource
— свойства глобального объекта. Следующая функция определяет, доступен ли конструктор EventSource
в браузере. Если функция возвращает true
, то могут использоваться отправленные сервером события. В противном случае следует использовать запасной механизм, такой как длинный опрос.
function supportsSSE() { return !!window.EventSource; }
соединительный
Чтобы подключиться к потоку событий, вызовите конструктор EventSource
, как показано ниже. Вы должны указать URL-адрес потока событий, на который вы пытаетесь подписаться. Конструктор автоматически позаботится об открытии соединения.
EventSource(url);
Onopen обработчик событий
Когда соединение установлено, onopen
обработчик события onopen
. Обработчик события принимает событие open
как единственный аргумент. Общий onopen
события onopen
показан в следующем примере.
source.onopen = function(event) { // handle open event };
Обработчики событий addEventListener()
также могут быть написаны с использованием addEventListener()
. Этот альтернативный синтаксис предпочтительнее onopen
поскольку он позволяет нескольким обработчикам присоединяться к одному и тому же событию. Предыдущий onopen
события onopen
был переписан ниже с использованием addEventListener()
.
source.addEventListener("open", function(event) { // handle open event }, false);
Получение сообщений
Клиент интерпретирует поток событий как последовательность событий message
DOM. Каждое событие, полученное с сервера, вызывает onmessage
обработчика события onmessage
. Обработчик onmessage
принимает событие message
качестве единственного аргумента. В следующем примере создается onmessage
события onmessage
.
source.onmessage = function(event) { var data = event.data; var origin = event.origin; var lastEventId = event.lastEventId; // handle message };
Событие message
содержит три важных свойства — data
, origin
и lastEventId
. Как следует из названия, data
содержат фактические данные сообщения в виде строки. Данные могут быть строкой JSON , которую можно передать в метод JSON.parse()
. Свойство origin
содержит окончательный URL-адрес потока событий после любых перенаправлений. Источник должен быть проверен, чтобы убедиться, что сообщения принимаются только из ожидаемых источников. Наконец, свойство lastEventId
содержит идентификатор последнего сообщения, увиденного в потоке событий. Сервер может прикреплять идентификаторы к отдельным сообщениям, используя это свойство. Если идентификатор никогда не был виден, тогда lastEventId
будет пустой строкой.
onmessage
события onmessage
также может быть написан с использованием addEventListener()
. В следующем примере показан предыдущий onmessage
события onmessage
, переписанный для использования addEventListener()
.
source.addEventListener("message", function(event) { var data = event.data; var origin = event.origin; var lastEventId = event.lastEventId; // handle message }, false);
Названные события
Один поток событий может указывать различные типы событий путем реализации именованных событий . Именованные события не обрабатываются обработчиком событий message
. Вместо этого каждый тип именованного события обрабатывается своим уникальным обработчиком. Например, если поток событий содержит события с именем foo
, то потребуется следующий обработчик событий. Обратите внимание, что обработчик события foo
идентичен обработчику события message
, за исключением типа события. Конечно, любые другие типы именованных сообщений потребуют отдельных обработчиков событий.
source.addEventListener("foo", function(event) { var data = event.data; var origin = event.origin; var lastEventId = event.lastEventId; // handle message }, false);
Обработка ошибок
Если возникает проблема с потоком событий, запускается обработчик события onerror
. Распространенной причиной ошибок является разрыв соединения. Хотя объект EventSource
автоматически пытается повторно подключиться к серверу, при отключении также генерируется событие ошибки. В следующем примере показан onerror
события onerror
.
source.onerror = function(event) { // handle error event };
Конечно, onerror
события onerror
также может быть переписан с использованием addEventListener()
, как показано ниже.
source.addEventListener("error", function(event) { // handle error event }, false);
Отсоединение
Соединение EventSource
может быть прервано клиентом в любое время, вызвав метод close()
. Синтаксис для close()
показан ниже. Метод close()
не принимает никаких аргументов и не возвращает никакого значения.
source.close();
Состояния подключения
Состояние соединения readyState
хранится в его readyState
. В любой точке своего жизненного цикла соединение может находиться в одном из трех возможных состояний — соединение, открытие и закрытие. Следующий список описывает каждое состояние.
- Соединение — когда объект
EventSource
создан, он изначально переходит в состояние соединения. В течение этого времени соединение еще не установлено.EventSource
также перейдет в состояние соединения, если установленное соединение потеряно. ЗначениеreadyState
дляEventSocket
в состоянии соединения равно 0. Это значение определяется как константаEventSource.CONNECTING
. - Открыто — Установленное соединение считается открытым. Объекты
EventSource
в открытом состоянии могут получать данные. ЗначениеreadyState
равное 1, соответствует открытому состоянию. Это значение определяется как константаEventSource.OPEN
. - Закрыто — считается, что
EventSource
находится в закрытом состоянии, если соединение не установлено и оно не пытается восстановить соединение. Это состояние обычно вводится путем вызова методаclose()
.EventSource
в закрытом состоянии имеет значениеreadyState
равное 2. Это значение определяется как константаEventSource.CLOSED
.
В следующем примере показано, как свойство readyState
можно использовать для проверки соединения EventSource
. Чтобы избежать жесткого кодирования значений readyState
, в примере используются константы состояния.
switch (source.readyState) { case EventSource.CONNECTING: // do something break; case EventSource.OPEN: // do something break; case EventSource.CLOSED: // do something break; default: // this never happens break; }
Вывод
В этой статье рассматривается клиентский аспект событий, отправляемых сервером. Если вы хотите узнать больше о событиях, отправленных сервером, я рекомендую прочитать Серверную часть событий, отправляемых сервером . Я также написал дополнительную статью о событиях, отправленных сервером, в Node.js. Наслаждайтесь!