推送事件

Matt Gaunt

至此,我们已介绍了如何为用户订阅和发送推送消息。下一步是在用户设备上接收此推送消息并显示通知(以及执行我们可能想要执行的任何其他工作)。

收到消息后,系统会在您的服务工作器中分派推送事件。

用于设置推送事件监听器的代码应与您用 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.');
    }
});

对于刚开始接触 Service Worker 的大多数开发者来说,此代码中最奇怪的部分是 self 变量。self 通常用于 Web Worker(Service Worker 就是 Web Worker)。self 是指全局作用域,有点类似于网页中的 window。但对于 Web Worker 和 Service Worker,self 是指 worker 本身。

在上面的示例中,可以将 self.addEventListener() 视为向 Service Worker 本身添加事件监听器。

在推送事件示例中,我们会检查是否有任何数据,并将一些内容输出到控制台。

您还可以通过其他方式解析推送事件中的数据:

// 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()

大多数人使用 json()text(),具体取决于他们对应用的预期。

此示例演示了如何添加推送事件监听器以及如何访问数据,但它缺少两项非常重要的功能。它不会显示通知,也不会使用 event.waitUntil()

等待到

关于 Service Worker 需要了解的一件事是,您对 Service Worker 代码何时运行几乎没有控制权。浏览器决定何时唤醒它以及何时终止它。您唯一能告诉浏览器“嘿,我正在忙着做重要的事情”的方法是将 Promise 传递给 event.waitUntil() 方法。这样,浏览器会让服务工作线程保持运行状态,直到您传入的 promise 已解决。

对于推送事件,还有一项额外的要求,即您必须在传入的 promise 解决之前显示通知。

下面是一个显示通知的基本示例:

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

    event.waitUntil(promiseChain);
});

调用 self.registration.showNotification() 是向用户显示通知的方法,它会返回一个 Promise,该 Promise 会在显示通知后解析。

为了尽可能让此示例保持清晰,我将此 promise 分配给了一个名为 promiseChain 的变量。然后将其传入 event.waitUntil()。我知道这很冗长,但我发现很多问题都是由于误解了应传入 waitUntil() 的内容或由于 promise 链断开而导致的。

一个更复杂的示例是,发出网络请求以获取数据,并使用 Google Analytics 跟踪推送事件,如下所示:

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);
});

在这里,我们调用一个返回 Promise pushReceivedTracking() 的函数。为了方便起见,我们假设该函数会向分析服务提供商发出网络请求。我们还会发出网络请求、获取响应,并使用响应数据显示通知的标题和消息。

我们可以将这些 promise 与 Promise.all() 结合使用,确保在执行这两项任务时,服务工件保持活跃状态。生成的 promise 会传入 event.waitUntil(),这意味着浏览器会等待两个 promise 都完成,然后再检查是否已显示通知并终止服务工作器。

我们之所以应该关注 waitUntil() 及其用法,是因为开发者最常遇到的一个问题是,当 promise 链不正确 / 损坏时,Chrome 会显示以下“默认”通知:

Chrome 中的默认通知的图片

只有在收到推送消息且服务工件中的推送事件在传递给 event.waitUntil() 的 promise 完成后显示通知时,Chrome 才会显示“此网站已在后台更新”通知。

开发者会遇到此问题的主要原因是,他们的代码会经常调用 self.registration.showNotification(),但不会对其返回的 Promise 执行任何操作。这会间歇性地显示默认通知。例如,我们可以移除上述示例中对 self.registration.showNotification() 的返回,这样就有可能看到此通知。

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);
});

您可以看到,这很容易被忽略。

请注意,如果您看到该通知,请检查您的 promise 链和 event.waitUntil()

在下一部分中,我们将介绍如何设置通知样式以及可以显示哪些内容。

下一步做什么

Codelab