• How JavaScript Works: The Mechanics of Web Push Notifications
  • Originally written by Alexander Zlatkov
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Starrier
  • Proofread by: Allen, old Professor

This is chapter 9 in a series of articles devoted to JavaScript and its building components. Along the way of identifying and describing the core elements, we also shared some rules of thumb we used to build a lightweight JavaScript application, SessionStack, that was robust, high-performance, and could help users see and reproduce their Web application flaws in real time.

If you missed the first few chapters, you can find them here:

  1. How JavaScript works: An overview of the engine, runtime, call stack
  2. How JavaScript works: 5 Tips for Optimizing Code in V8
  3. How does JavaScript work: Memory management + Handling four common memory leaks
  4. How JavaScript works: Event loops and the rise of asynchronous programming + 5 tips for better async/await coding
  5. How JavaScript works: Take a deep dive into WebSockets and HTTP/2 with SSE, and make the right choice between the two
  6. How JavaScript works: To compete with WebAssembly why WebAssembly is better than JavaScript in some situations
  7. How does JavaScript work: The internals of Web workers and 5 scenarios in which you should use it
  8. How does JavaScript work: The Web Worker lifecycle and use cases

Today, we’ll focus on Web push notifications: we’ll look at their building components, explore the process of sending/receiving notifications, and finally share how SessionStack is leveraging these to build new product features.

Push notifications are widely used in the mobile space. For some reason, they came late to the Web, despite long calls from developers.

An overview of the

Web push notifications allow users to opt-in to receive updates within a Web application that are often interesting, important, and real-time to the user to re-engage the user base.

Push is based on the Service Worker we discussed in detail in the previous article.

In this case, Service workers are used because they work in the background. This is useful for push notifications because it means that their code is executed only when the user interacts with the notifications themselves.

Push and notification

Push and notification are two different apis.

  • Push – this is called when the server pushes a message to the Service Worker
  • Notifications – This is the action of a script in a Service Worker or Web application that displays information to the user.

push

There are three steps to implementing push:

  1. UI – Add the necessary client-side logic to get users to subscribe to the push, which is the JavaScript logic your Web application UI needs so that users can register themselves to receive the push.
  2. Send push message – Implement an API call on the server that triggers a push message to the user’s device.
  3. Accept push messages – Once a push message reaches the browser, it is processed.

We will now describe the whole process in more detail.

Browser Support detection

First, we need to check whether the current browser supports push messages. There are two simple ways to check whether push messages are supported:

  1. checknavigatorOn the objectserviceWorker
  2. checkwindowOn the objectPushManager

Both tests look like this:

if(! ('serviceWorker' in navigator)) { 
  // Service Worker isn't supported on this browser, disable or hide UI. return; } if (! ('PushManager' in window)) { // Push isn't supported on this browser, disable or hide UI. 
  return; 
}
Copy the code

Register a Service Worker

At this point, we know that the feature is supported. The next step is to register our Service Worker.

You should be familiar with how to register a Service Worker from our previous article.

Ask for permission

After registering the Service Worker, we can start subscribing users. To do this, we need his permission to send him a push message.

The licensed API is relatively simple, but the downside is that the API has changed from accepting callbacks to returning promises, which presents a problem: we can’t tell which version of the API is implemented by the current browser, so you have to implement and deal with both versions.

It looks like this:

function requestPermission() {
  return new Promise(function(resolve, reject) {
    const permissionResult = Notification.requestPermission(function(result) {
      // Handling deprecated version with callback.
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  })
  .then(function(permissionResult) {
    if(permissionResult ! = ='granted') {
      throw new Error('Permission not granted.'); }}); }Copy the code

Notification. RequestPermission () call will be displayed to the user the following tips:

Once granted, turned off, or blocked, we get the result in string format: ‘granted’, ‘default’, or ‘denied’.

Remember, if the user clicks the Block button, your Web application will not be able to request permission from the user again until they manually “unblock” your application by changing the permission status. This option is hidden in the Settings screen.

Users subscribe using PushManager

Once we register the Service Worker and get the permissions, when you register your Service Worker, we can call registration. PushManager. The subscribe () to subscribers.

The entire fragment might look like this (including Service Workder registration) :

function subscribeUserToPush() {
  return navigator.serviceWorker.register('service-worker.js')
  .then(function(registration) {
    var subscribeOptions = {
      userVisibleOnly: true,
      applicationServerKey: btoa(
        'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U')};return registration.pushManager.subscribe(subscribeOptions);
  })
  .then(function(pushSubscription) {
    console.log('PushSubscription: ', JSON.stringify(pushSubscription));
    return pushSubscription;
  });
}
Copy the code

Registration. PushManager. Subscribe (options) to accept an options object, which contains necessary and optional parameters:

  • userVisibleOnly: Boolean indicates that the returned push subscription will only be used for messages visible to the user. It must be set totrueOtherwise you will get an error (for historical reasons).
  • applicationServerKey: Base64 encodedDOMStringorArrayBufferContains the public key that the push server uses to authenticate the application server.

Your server needs to generate a pair of application server keys — also known as VAPID keys — that are unique to your server. They are a pair of public and private keys. The private key is secretly stored on your terminal, while the public key is exchanged with the client. These keys allow the push service to know which application server has subscribed to the user and ensure that it is the same server that triggered the push to that particular user.

You only need to create a private/public key pair for your application once. The way to do this is to do this — web-push-codelab.glitch.me/.

The browser passes the applicationServerKey (public key) to the push service when subscribing to the user, which means that the push service can bind the application’s public key to the user’s PushSubscription.

Here’s what happened:

  • You can call it when your Web app is loadedsubscribe()To pass in your server key.
  • The browser makes a request to the push service that generates the endpoint, associates the endpoint with the key and returns the endpoint to the browser.
  • The browser adds this endpoint toPushSubscriptionObject, the object passessubscribe()The promise returns.

Then, whenever you want to send a push message, you need to create an Authorization header that contains the signature information using the application server’s private key. When the push service receives a request to send a push message, it verifies the header by looking for the public key already connected to that particular endpoint (step 2).

Push object

PushSubscription contains all the information a user’s device needs to send a push message. Something like this:

{
  "endpoint": "https://domain.pushservice.com/some-id"."keys": {
    "p256dh":
"BIPUL12DLfytvTajnryr3PJdAgXS3HGMlLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WArAPIxr4gK0_dQds4yiI="."auth":"FPssMOQPmLmXWmdSTdbKVw=="}}Copy the code

Endpoint is the URL of the push service. To trigger a push message, send a POST request to this URL.

The value of the keys object is used to encrypt the message data brought by the push message.

Once the user is subscribed and you have PushSubscription, you need to send it to your server. There (on the server), you save the subscription to the database and use it from now on if you want to push a message to that user.

Send push message

When you want to send push messages to users, you first need a push service. You tell the push service (via API calls) what data to send, who to receive it, and other standards about how to send it. Typically, this API call is made on your server.

Push service

The push service receives the request, validates the request, and delivers the push message to the corresponding browser.

Note that the push service is not managed by you — it’s a third-party service. Your server communicates with the push service through an API. An example of a push service is Google’s FCM.

Push services handle all the heavy lifting. For example, if the browser is offline, push services queue messages for the browser to come online again before sending them.

Each browser uses whatever push service they want, which is out of the developer’s control.

However, all push services have the same API, so the implementation process should not be too difficult.

To get the URL for the push request, you need to check the endpoint value stored in the PushSubscription object.

Push service API

The push service API provides a way to send messages to users. API is based on the Web Push protocol, which is an IETF standard that defines how to make API calls to push services.

The data you send using push messages must be encrypted. This prevents push services from seeing the sent data. This is important because the browser can decide which push service to use (it may be using some untrusted and insecure server).

For each push message, you can also provide the following instructions:

  • TTL – defines how long a message will wait in the queue before it is deleted without being pushed.
  • Priority – Define the priority of the messages, because the push service only sends high-priority messages to protect the battery life of the user’s device.
  • Topic – Give push messages a Topic, and new messages will replace waiting messages with the same Topic, so that users will not receive outdated messages once the device is active.

Push events in the browser

As mentioned above, after a message is sent to the push service, it stays on hold until one of the following happens:

  • The device goes online.
  • The message expired on the queue due to TTL.

When the push Service delivers a message, the browser receives it, decrypts it, and distributes a push event in the Service Worker.

Best of all, the browser can execute your Service Worker even if your web page is not open. Here’s what will happen:

  • The push message arrives at the browser that decrypts it
  • The browser wakes up the Service Worker
  • pushEvents are distributed to the Service Worker

The code to set up the push event listener should be similar to any other event listener written in 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.'); }});Copy the code

One thing to know about Service workers is that you have no control over how long the Service Worker code runs. The browser decides when to wake it up and when to terminate it.

In Service Workers, event.waitUntil(Promise) informs the browser that work is in progress until the promise is confirmed, and it should not terminate the Sercice worker if it wants to complete the work.

Here is an example of handling push events:

self.addEventListener('push'.function(event) {
  var promise = self.registration.showNotification('Push notification! ');

  event.waitUntil(promise);
});
Copy the code

Call self. Registration. ShowNotification () will send a notification to the user, and returns a promise, as long as the news shows this promise will trigger the resolve.

The showNotification(title, options) method can be visually adjusted to suit your needs. The title argument is a string, and options is an object that looks something like this:

{
  "/ /": "Visual Options"."body": "<String>"."icon": "<URL String>"."image": "<URL String>"."badge": "<URL String>"."vibrate": "<Array of Integers>"."sound": "<URL String>"."dir": "<String of 'auto' | 'ltr' | 'rtl'>"."/ /": "Behavioural Options"."tag": "<String>"."data": "<Anything>"."requireInteraction": "<boolean>"."renotify": "<Boolean>"."silent": "<Boolean>"."/ /": "Both Visual & Behavioural Options"."actions": "<Array of Strings>"."/ /": "Information Option. No visual affect."."timestamp": "<Long>"
}
Copy the code

You can read more details about each option here – developer.mozilla.org/en-US/docs/… .

Push notifications are a great way to grab users’ attention when urgent, important, and time-sensitive information needs to be shared with them.

For example, in SessionStack, we plan to use push notifications to alert users to when a crash, problem, or exception is occurring in their product. This lets the user know immediately that there is a problem. They can then use the data collected by our library (such as DOM modifications, user interactions, network requests, unhandled exceptions, and debugging information) to recreate the problem in video form and see everything that ends up happening to the user.

This feature not only helps customers understand and reproduce any problems, but also notifies customers the first time a problem occurs.

If you want to try SessionStack, here’s a free plan.

resources

  • Developers.google.com/web/fundame…
  • Developers.google.com/web/fundame…
  • Developers.google.com/web/fundame…
  • Developers.google.com/web/fundame…

The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.