推送事件

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

推送事件

当收到消息时,将在您的 Service Worker 中分发一个推送事件。

用于设置推送事件侦听器的代码应与您在 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 Workers 中使用,而 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()

Wait Until

关于 Service Worker,需要理解的一点是,您几乎无法控制 Service Worker 代码何时运行。浏览器决定何时唤醒它以及何时终止它。您告诉浏览器的唯一方法是“嘿,我正忙着做重要的事情”,就是将 Promise 传递到 event.waitUntil() 方法中。这样,浏览器将保持 Service Worker 运行,直到您传入的 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 链断裂而导致的。

一个更复杂的示例,其中包含数据网络请求以及使用分析跟踪推送事件,可能如下所示

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() 结合使用,确保在完成这两个任务时 Service Worker 保持活动状态。生成的 Promise 将传递到 event.waitUntil() 中,这意味着浏览器将等待两个 Promise 都完成后,再检查是否已显示通知并终止 Service Worker。

我们应该关注 waitUntil() 以及如何使用它的原因是,开发者面临的最常见问题之一是,当 Promise 链不正确/断裂时,Chrome 会显示此“默认”通知

An Image of the default notification in Chrome

仅当收到推送消息且 Service Worker 中的推送事件在传递给 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()

在下一节中,我们将研究我们可以做些什么来设置通知样式以及我们可以显示哪些内容。

下一步去哪里

代码实验室