Usando eventos enviados pelo servidor
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since January 2020.
O desenvolvimento de um aplicativo da Web que usa eventos enviados pelo servidor é simples. Você precisará de um pouco de código no servidor para transmitir eventos para o front-end, mas o código do lado do cliente funciona quase de forma idêntica a websockets em parte do tratamento eventos de entrada. Esta é uma conexão unidirecional, portanto, você não pode enviar eventos de um cliente para um servidor.
Recebendo eventos do servidor
A API de eventos enviados pelo servidor está contida na interface EventSource
; para abrir uma conexão com o servidor para começar a receber eventos dele, crie um novo objeto EventSource
com a URL de um script que gera os eventos. Por exemplo:
const evtSource = new EventSource("ssedemo.php");
Se o script do gerador de eventos estiver hospedado em uma origem diferente, um novo objeto EventSource
deve ser criado com a URL e um dicionário de opções. Por exemplo, supondo que o script do cliente esteja em example.com
:
const evtSource = new EventSource("//api.example.com/ssedemo.php", {
withCredentials: true,
});
Depois de instanciar a origem do evento, você pode começar a ouvir as mensagens do servidor anexando um manipulador para o evento message
:
evtSource.onmessage = (event) => {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");
newElement.textContent = `message: ${event.data}`;
eventList.appendChild(newElement);
};
Este código escuta as mensagens recebidas (ou seja, avisos do servidor que não possuem um campo event
) e anexa o texto da mensagem a uma lista no HTML do documento.
Você também pode escutar eventos com addEventListener()
:
evtSource.addEventListener("ping", (evento) => {
const novoElemento = document.createElement("li");
const eventList = document.getElementById("list");
const time = JSON.parse(event.data).time;
newElement.textContent = `ping em ${time}`;
eventList.appendChild(newElement);
});
Este código é semelhante, exceto que será chamado automaticamente sempre que o servidor enviar uma mensagem com o campo event
definido como "ping"; ele então analisa o JSON no campo data
e gera essa informação.
Aviso: quando não usado em HTTP/2, o SSE sofre de uma limitação ao número máximo de conexões abertas, o que pode ser especialmente doloroso ao abrir várias guias, pois o limite é por navegador e é definido para um número muito baixo (6). O problema foi marcado como "Não será corrigido" no Chrome e no Firefox. Esse limite é por navegador + domínio, o que significa que você pode abrir 6 conexões SSE em todas as guias para www.example1.com
e outras 6 conexões SSE para www.example2.com
(por Stackoverflow). Ao usar HTTP/2, o número máximo de streams HTTP simultâneos é negociado entre o servidor e o cliente (o padrão é 100).
Enviando eventos do servidor
O script do lado do servidor que envia eventos precisa responder usando o tipo MIME text/event-stream
. Cada notificação é enviada como um bloco de texto finalizado por um par de novas linhas. Para obter detalhes sobre o formato do fluxo de eventos, consulte Formato do fluxo de eventos.
O código PHP para o exemplo que estamos usando aqui segue:
date_default_timezone_set("America/New_York");
header("Cache-Control: no-store");
header("Content-Type: text/event-stream");
$counter = rand(1, 10);
while (true) {
// A cada segundo, envia um evento "ping".
echo "evento: ping\n";
$curDate = date(DATE_ISO8601);
echo 'data: {"time": "' . $curDate . '"}';
echo "\n\n";
// Envia uma mensagem simples em intervalos aleatórios.
$counter--;
if (!$counter) {
echo 'data: Esta é uma mensagem no momento ' . $curDate . "\n\n";
$counter = rand(1, 10);
}
ob_end_flush();
flush();
// Interrompe o loop se o cliente abortou a conexão (fechou a página)
if (connection_aborted()) break;
sleep(1);
}
O código acima gera um evento a cada segundo, com o tipo de evento "ping". Os dados de cada evento são um objeto JSON contendo o carimbo de data/hora ISO 8601 correspondente à hora em que o evento foi gerado. Em intervalos aleatórios, uma mensagem simples (sem tipo de evento) é enviada. O loop continuará funcionando independentemente do status da conexão, portanto, uma verificação é incluída para quebrar o loop se a conexão foi fechada (por exemplo, o cliente fecha a página).
você pode encontrar um exemplo completo que usa o código mostrado neste artigo no GitHub — veja [Simple SSE demo using PHP](https://github.com/mdn/dom-examples/tree/main /servidor-enviados-eventos).
Manipulação de erros
Quando ocorrem problemas (como um tempo limite de rede ou problemas relacionados a controle de acesso), um evento de erro é gerado. Você pode agir sobre isso programaticamente implementando o retorno de chamada onerror
no objeto EventSource
:
evtSource.onerror = (err) => {
console.error("EventSource falhou:", err);
};
Fechando streams de eventos
Por padrão, se a conexão entre o cliente e o servidor for fechada, a conexão será reiniciada. A conexão é finalizada com o método .close()
.
evtSource.close();
Formato do fluxo de eventos
O fluxo de eventos é um fluxo simples de dados de texto que deve ser codificado usando UTF-8. As mensagens no fluxo de eventos são separadas por um par de caracteres de nova linha. Dois pontos como o primeiro caractere de uma linha é essencialmente um comentário e é ignorado.
Nota: A linha de comentário pode ser usada para evitar que as conexões atinjam o tempo limite; um servidor pode enviar um comentário periodicamente para manter a conexão ativa.
Cada mensagem consiste em uma ou mais linhas de texto listando os campos dessa mensagem. Cada campo é representado pelo nome do campo, seguido por dois pontos, seguido pelos dados de texto para o valor desse campo.
Campos
Cada mensagem recebida tem alguma combinação dos seguintes campos, um por linha:
event
-
Uma string que identifica o tipo de evento descrito. Se isso for especificado, um evento será despachado no navegador para o ouvinte para o nome do evento especificado; o código-fonte do site deve usar
addEventListener()
para ouvir eventos nomeados. O manipuladoronmessage
é chamado se nenhum nome de evento for especificado para uma mensagem. data
-
O campo de dados para a mensagem. Quando o
EventSource
recebe várias linhas consecutivas que começam comdata:
, ele as concatena, inserindo um caractere de nova linha entre cada uma. As novas linhas à direita são removidas. id
-
o ID do evento para definir o último valor do ID do evento do objeto
EventSource
. retry
-
O tempo de reconexão. Se a conexão com o servidor for perdida, o navegador aguardará o tempo especificado antes de tentar se reconectar. Deve ser um número inteiro, especificando o tempo de reconexão em milissegundos. Se um valor não inteiro for especificado, o campo será ignorado.
Todos os outros nomes de campo são ignorados.
Nota: se uma linha não contiver dois pontos, a linha inteira será tratada como o nome do campo com uma string de valor vazia.
Exemplos
Mensagens somente de dados
No exemplo a seguir, há três mensagens enviadas. O primeiro é apenas um comentário, pois começa com dois pontos. Como mencionado anteriormente, isso pode ser útil como um mecanismo de manutenção se as mensagens não forem enviadas regularmente.
A segunda mensagem contém um campo de dados com o valor "algum texto". A terceira mensagem contém um campo de dados com o valor "outra mensagem\ncom duas linhas". Observe o caractere especial de nova linha no valor.
: este é um fluxo de teste
dados: algum texto
dados: outra mensagem
dados: com duas linhas
Eventos nomeados
Este exemplo envia eventos nomeados. Cada um tem um nome de evento especificado pelo campo event
e um campo data
cujo valor é uma string JSON apropriada com os dados necessários para que o cliente atue no evento. O campo data
pode, é claro, ter qualquer string de dados; não precisa ser JSON.
evento: userconnect
data: {"username": "bobby", "time": "02:33:48"}
evento: mensagem do usuário
data: {"username": "bobby", "time": "02:34:11", "text": "Olá a todos."}
evento: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}
evento: mensagem do usuário
data: {"username": "sean", "time": "02:34:36", "text": "Tchau, bobby."}
Misturando e combinando
Você não precisa usar apenas mensagens sem nome ou eventos digitados; você pode misturá-los em um único fluxo de eventos.
evento: userconnect
data: {"username": "bobby", "time": "02:33:48"}
data: Aqui está uma mensagem do sistema de algum tipo que será usada
dados: para realizar alguma tarefa.
evento: mensagem do usuário
data: {"username": "bobby", "time": "02:34:11", "text": "Olá a todos."}
Especificações
Specification |
---|
HTML Standard # the-eventsource-interface |
Compatibilidade com navegadores
BCD tables only load in the browser