Eventos push

Até agora, abordamos a inscrição de um usuário e o envio de uma mensagem push. A próxima etapa é receber essa mensagem push no dispositivo do usuário e mostrar uma notificação (assim como fazer qualquer outro trabalho que quisermos fazer).

O evento push

Quando uma mensagem é recebida, um evento push é enviado no service worker.

O código para configurar um listener de eventos push precisa ser bastante semelhante a qualquer outro listener de eventos que você escreveria em JavaScript:

self.addEventListener('push', function(event) {
    if (event.data) {
    console.log('This push event has data: ', event.data.text());
    } else {
    console.log('This push event has no data.');
    }
});

A parte mais estranha desse código para a maioria dos desenvolvedores que são iniciantes em service workers é a variável self. self é usado com frequência em workers da Web, que são service workers. self se refere ao escopo global, mais ou menos como window em uma página da Web. No entanto, para workers da Web e service workers, self se refere ao worker em si.

No exemplo acima, self.addEventListener() pode ser considerado como a adição de um listener de evento ao próprio service worker.

No exemplo de evento push, verificamos se há dados e imprimimos algo no console.

Há outras maneiras de analisar os dados de um evento push:

// Returns string
event.data.text()

// Parses data as JSON string and returns an Object
event.data.json()

// Returns blob of data
event.data.blob()

// Returns an arrayBuffer
event.data.arrayBuffer()

A maioria das pessoas usa json() ou text(), dependendo do que esperam do aplicativo.

Este exemplo demonstra como adicionar um listener de evento push e como acessar dados, mas ele não tem duas partes muito importantes da funcionalidade. Ele não mostra uma notificação e não usa event.waitUntil().

Esperar até

Uma das coisas a entender sobre os service workers é que você tem pouco controle sobre quando o código do service worker será executado. O navegador decide quando ativá-lo e quando encerrar. A única maneira de dizer ao navegador: "Ei, estou muito ocupado fazendo coisas importantes" é transmitindo uma promessa para o método event.waitUntil(). Com isso, o navegador vai manter o service worker em execução até que a promessa transmitida seja resolvida.

Com eventos push, há um requisito adicional de exibir uma notificação antes que a promessa transmitida seja resolvida.

Confira um exemplo básico de exibição de uma notificação:

self.addEventListener('push', function(event) {
    const promiseChain = self.registration.showNotification('Hello, World.');

    event.waitUntil(promiseChain);
});

A chamada self.registration.showNotification() é o método que mostra uma notificação para o usuário e retorna uma promessa que será resolvida quando a notificação for exibida.

Para manter este exemplo o mais claro possível, atribuí essa promessa a uma variável chamada promiseChain. Depois, ela é transmitida para event.waitUntil(). Sei que isso é muito detalhado, mas já vi vários problemas que culminaram em um mal-entendido sobre o que precisa ser transmitido para waitUntil() ou como resultado de cadeias de promessas quebradas.

Um exemplo mais complicado com uma solicitação de rede para dados e rastreamento do evento push com análise pode ser parecido com este:

self.addEventListener('push', function(event) {
    const analyticsPromise = pushReceivedTracking();
    const pushInfoPromise = fetch('/api/get-more-data')
    .then(function(response) {
        return response.json();
    })
    .then(function(response) {
        const title = response.data.userName + ' says...';
        const message = response.data.message;

        return self.registration.showNotification(title, {
        body: message
        });
    });

    const promiseChain = Promise.all([
    analyticsPromise,
    pushInfoPromise
    ]);

    event.waitUntil(promiseChain);
});

Aqui, estamos chamando uma função que retorna uma promessa pushReceivedTracking(), que, para fins de exemplo, podemos fingir que fará uma solicitação de rede para nosso provedor de análise. Também fazemos uma solicitação de rede, recebemos a resposta e mostramos uma notificação usando os dados de respostas para o título e a mensagem da notificação.

Podemos garantir que o worker do serviço seja mantido ativo enquanto essas duas tarefas são realizadas combinando essas promessas com Promise.all(). A promessa resultante é transmitida para event.waitUntil(), o que significa que o navegador vai esperar até que as duas promessas sejam concluídas antes de verificar se uma notificação foi mostrada e encerrar o service worker.

O motivo pelo qual precisamos nos preocupar com waitUntil() e como usá-lo é que um dos problemas mais comuns que os desenvolvedores enfrentam é que, quando a cadeia de promessas está incorreta ou quebrada, o Chrome mostra esta notificação "padrão":

Uma imagem da notificação padrão no Chrome

O Chrome só vai mostrar a notificação "Este site foi atualizado em segundo plano" quando uma mensagem push for recebida e o evento push no worker do serviço não mostrar uma notificação depois que a promessa transmitida para event.waitUntil() for concluída.

O principal motivo pelo qual os desenvolvedores são pegos é que o código muitas vezes chama self.registration.showNotification(), mas não faz nada com a promessa que ele retorna. Isso faz com que a notificação padrão seja mostrada de forma intermitente. Por exemplo, poderíamos remover o retorno de self.registration.showNotification() no exemplo acima e correr o risco de ver esta notificação.

self.addEventListener('push', function(event) {
    const analyticsPromise = pushReceivedTracking();
    const pushInfoPromise = fetch('/api/get-more-data')
    .then(function(response) {
        return response.json();
    })
    .then(function(response) {
        const title = response.data.userName + ' says...';
        const message = response.data.message;

        self.registration.showNotification(title, {
        body: message
        });
    });

    const promiseChain = Promise.all([
    analyticsPromise,
    pushInfoPromise
    ]);

    event.waitUntil(promiseChain);
});

É fácil perder isso de vista.

Se você receber essa notificação, verifique as cadeias de promessas e event.waitUntil().

Na próxima seção, vamos analisar o que podemos fazer para estilizar notificações e o conteúdo que podemos exibir.

A seguir

Code labs