Why were web workers removed from Android

Use service workers

Experimental

This is an experimental technology
As this technology has not yet been definitively implemented, browser compatibility should be considered. It is also possible that the syntax will be changed in a later specification.

This article provides information on how to get started with service workers, including the basic architecture, registering a service worker, installing and activating a new service worker, updating a service worker, cache control and custom responses, all in Context of a simple app with offline functionality.

The requirement for service workers

A major problem that web users have suffered for years is loss of internet connectivity. Even the best web application provides a terrible user experience if it cannot be downloaded. There have been various attempts to create technologies that will solve this problem (as our offline page shows) and some issues have been addressed in this way. The biggest problem, however, is that there is still no good general control mechanism for asset caching and own network requests.

The previous attempt - AppCache - seemed a good idea as it allowed the developer to specify assets that should be easy to cache. However, the technology made assumptions about what the developer was planning to do and failed in several places if the web application did not follow those assumptions one-to-one. More details can be found in Jake Archibald's "Application Cache is a Douchebag".

Notice: If AppCache is used to provide offline support for a site, a warning has been displayed on the console since Firefox 44, advising developers to use service workers instead (Bug 1204581).

Service workers should eventually fix these problems. Its syntax is more complex than that of the AppCache, but the advantage is that JavaScript can now be used to fine-tune the assumptions made by the AppCache, which solves the problem described above and many more. With the help of a service worker, the web application can easily be set up to use cached assets with priority and thus enable a certain user experience even in the offline case before further data is loaded from the network (also known as the offline first approach) . Native apps already provide this feature, which is why native apps are often preferred over web applications.

Facility for the use of service workers

Many of the properties of service workers are now set by default in newer versions of supporting browsers. However, if the sample code does not work in one of the installed browsers, it may be necessary to explicitly activate a setting:

  • Firefox Nightly: Navigate to and set to true; Then restart the browser.
  • Chrome Canary: Navigate to and enable; Then restart the browser (note that some features are now enabled by default in Chrome).
  • Opera: Navigate to and enable; Then restart the browser.

Also, make sure your code is sent over HTTPS - service workers are limited to HTTPS for security reasons. Since GitHub (or GitHub Pages) supports HTTPS, it's a good place to host experiments.

Basic architecture

A simple implementation of service workers usually follows the following steps:

  1. The URL of the service worker is called up and registered via.
  2. If this was successful, the service worker is executed in an (en-US); In principle, this is a special kind of worker context that runs outside of the main script's thread and has no access to the DOM.
  3. The service worker can now process events.
  4. The installation of the worker is attempted when pages managed by service workers are visited repeatedly. An install event is always the first to be sent to a service worker (this can be used, for example, to start the process that fills the IndexDB and caches page assets). This is the same process that takes place when installing a native or FirefoxOS app - preparing everything for offline use.
  5. Once the handler is complete, the service worker is considered installed.
  6. The next step is activation. As soon as the service worker has been installed, it receives an event. The main use of is to clean up resources that were used in previous versions of the service worker script.
  7. The service worker can now manage websites, but only if these were accessed after the successful completion of. A document that was started without a service worker will not be managed by a service worker until it is reloaded.

Promises

Promises are a very good mechanism for performing asynchronous operations, the success of which depends on one another. This technology is central to the way service workers work.

Promises enable many different things, but for our purpose it is sufficient to know that when a function returns a promise, a can be attached to the end of the call and provided with callbacks for success, failure, etc. It can also be used at the end to add a special callback for the failure.

Let us now compare the traditional synchronous callback structure with its asynchronous promise equivalent.

sync

async

In the first example we have to wait for the to be executed and returned before any further code can be executed. The second example returns a promise for, after which further code can be executed. When the promise is resolved, the code runs in asynchronous.

Now for a real-life example: what if we load images dynamically but want to make sure they don't appear until they're fully loaded? This is something that should be done frequently, but it can get very complicated. can be used to not display an image until it has been loaded. But what happens to events that take place before we can listen to them? One could try to circumvent this problem with the help of, but this is not reliable and does not cover the handling of several images at the same time. This approach is also still synchronous and thus blocks the main thread.

However, we can instead build our own promise to cover this case. (See our Promises test example for the appropriate source code, or here for a live application.)

Note:

A true service worker implementation would use caching and the event instead of the outdated XMLHttpRequest API. These features were not used here in order to be able to focus on promises.

We return a new promise using the constructor, which receives a callback function with - and - parameters as a parameter. Within this function we have to define what has to happen for the promise to be successfully resolved or rejected - in this case either a status is returned or not - and then to be called on success and failure. The rest of the content of this feature are steps on how to use XHR that we don't need to worry about right now.

When we get to the call of the function, we call it with the URL to the image we want to load, as expected, but the rest of the code is a bit different:

The method is appended to the end of the function call and contains two functions - the first is executed when the promise is resolved, the second when it is rejected. In the case of resolution, we display the image within and append it to the body (the argument of the function is the request.response within the resolve method of the promise); in the event of rejection, we output an error in the console.

All of this happens asynchronously.

Notice: Promises can be chained together, for example:

Service worker demo

To demonstrate the basics of registering and installing a service worker, we created a small demo called sw-test, which is a manageable Star Wars Lego picture gallery. It uses a Promise-supported function to read image data from a JSON object and load these images with the help of Ajax before they are displayed one below the other on the website. The application is static and kept simple. It also registers, installs and activates a service worker and caches the necessary files for offline use, if further aspects of this specification are activated in the calling browsers.




The source code can be found on GithHub, a live version can be viewed here. The aspect we want to look at here is the Promise (see app.js lines 17-42), which is a modified version of what was in the Promises test demo. The differences are as follows:

  1. In the original, we only submitted a URL to an image that we want to load. In this version we are submitting a JSON fragment that contains all the data for a single image (you can take a look at it in image-list.js). This happens because, because of the asynchronicity, the entire data has to be passed into the promise every time the promise is resolved. If only the URL were passed in and an attempt was made to access other things in the JSON during the loop, this would fail because the promise would not resolve at the same time as the iteration was completed, as this is a synchronous process.
  2. We also resolve the promise with an array, since we want to make the loaded image blob, but also the image name, picture credits and alt text available to the resolving function later. (see app.js lines 26-29). Promises resolve with a single argument, so if several values ​​are required, this must be resolved via an array or object.
  3. In order to access the values ​​of the resolved promise, we access the same function. (See app.js lines 55-59.) This may seem strange at first, but it is the correct way to handle promises.

Service workers on stage, please!

Let's go to service workers!

Register service workers

The first block of code in our app's JavaScript file - - looks like this. This is our starting point for using service workers.

  1. The outer block takes care of feature detection to ensure that service workers are supported before one is registered.
  2. Next we use the function to register the service worker for this page. This is just a JavaScript file within our app. (Note that the URL of the file is relative to the origin, not the JavaScript file it references.)
  3. The parameter is optional and can be used to specify the part of the. Restrict the application that the service worker should control. In this case, 'we' have specified what includes all the content under the app source directory. If the parameter is omitted, this directory would be selected by default. However, we have specified it here for illustrative purposes.
  4. The promise function is used to attach the success case to our promise structure. If the promise resolves successfully, the program code within this function is executed.
  5. Finally, we add the function to the end, which is executed if the promise is rejected.

A service worker is now registered who is running in a worker context and therefore has no access to the DOM. Code in the service worker is executed outside of the normal pages to control their loading.

A single service worker can control many pages. If a page is loaded within the specified code, the service worker is set up for this and starts working. It should therefore not be forgotten that global variables in the service worker script must be handled carefully: Each page in the scope has the same worker with whom it works.

Notice: Service workers work like proxy servers, which allows, among other things, to modify network requests and responses and replace them with objects from the cache.

Notice: A good thing about service workers is that when feature detection is used as in the example above, browsers that do not support service workers can still use the app normally and as expected. If AppCache and service worker are still used together on the same page, browsers that support AppCache but not service workers will use the former. Browsers that support both will ignore AppCache in favor of service workers.

Why is my service worker failing to register?

This can happen for the following reasons:

  1. The application does not run under HTTPS
  2. The path to the service worker file is not correctly described - it must be written relative to the origin, not relative to the root directory of the app. In our example, the worker is below, whereas the root directory is the app. So the path must be written as a, and not as a.
  3. The referenced service worker has a different origin than the app. That is also not permitted.

Please also note:

  • The service worker will only pick up network requests from clients within his scope.
  • The maximum scope of a service worker is the location of the worker himself.
  • If the service worker is active on a client that is delivered with the header, a list of maximum scopes can be defined for this worker.
  • Service worker APIs are hidden in Firefox and cannot be used when users are in incognito mode.

Installation and activation: filling the cache

After the service worker has been registered, the browser tries to install and activate the service worker for the corresponding page.

The install event is sent when an installation has been successfully completed. This event is usually used to fill the browser's offline cache with the assets that are needed for the app to run offline. To achieve this, we use the service worker storage API - a global object of the service worker, which allows us to store assets that have been delivered by network responses. This API works in a similar way to the browser's internal cache, but is domain-specific and exists until the contrary is stated, which again allows full control.

Notice: The Cache API is not supported in every browser. (See also the Browser Compatibility section for more information.) If you want to use it now, you can consider a polyfill, such as that contained in Google's Topeka demo, or you can save the assets in IndexedDB.

We'll start this section by looking at an example code - this is the first block in our service worker:

  1. Here an event listener is added to the service worker (with the help of), and the (en-US) method is appended to the event - this ensures that the service worker is not installed until the code within successfully executes has been.
  2. Within, we use the method to create a new cache with the name, which will be the first version of our site's resource store. This returns a promise for the cache created; as soon as this is resolved, we call a function which in turn calls the created cache and contains as a parameter an array with the URLs of the resources to be cached relative to the origin.
  3. If the promise is rejected, the installation fails and the worker does nothing. This is fine and provides an opportunity to fix the code and try again the next time you register.
  4. After a successful installation, the service worker is activated. If the service worker was installed and activated for the first time, this is of no further importance, but is important if the service worker is activated. (See the Updating the Service Worker section later.)

Notice: localStorage works similarly to the service worker cache, but is synchronous and therefore not allowed in service workers.

Notice: IndexedDB can be used within a service worker if necessary.

Own responses to network inquiries

Now that the page's assets have been cached, the service workers can now be told what to do with the cached content. This is done with the help of the event.

An event is dispatched every time a resource that is controlled by the service worker (i.e. the documents within his scope and the resources referenced there) is loaded. (So ​​when a cross-origin request is sent to an embedded image, that goes through the service worker as well.)

An event listener can be added to the service worker and the method of the event called to intercept the HTTP responses and replace them with our own.

You can start answering each time with the resource whose URL matches that of the network request:

allows us to replace every resource requested by the network with the corresponding resource in the cache, if it is available. The comparison takes place as with normal HTTP requests via URLs and headers.

Let's look at some more options we have if we want to define our own answers (see also the Fetch API documentation for more information on (en-US) and objects.)

  1. The constructor allows us to create a custom answer. In this case we are returning a simple string:

  2. The following more complex example shows that a number of headers can optionally be set in the response, which emulate the standard HTTP response headers. Here, the browser is only informed of the content type of our artificial response:

  3. If no match was found in the cache, the browser could be instructed to execute the standard network request with (en-US) to load the resource from the network, if it is available:

  4. If there was no hit and the network is also unavailable, the request can be answered with an alternate page using (en-US) as follows:

  5. Much information about each request can be obtained by calling parameters of the (en-US) object returned by the (en-US).

Dealing with failed cache requests

is useful when there is a hit in the service worker cache. But what if that is not the case? If we did not provide a way to handle a failed cache request, our promise would be rejected and we would face a network error if there was no cache hit.

Fortunately, the promise-based structure of the service worker allows further options to be provided. For example:

If the promise is rejected, the function returns the standard network request for the resource, which allows it to be downloaded from the server should a network connection exist.

Another way would be to not just load the resource from the network, but to store it in the cache for future requests so that they can also be used offline. That means, should further images be added to the Star Wars gallery, our app can load and cache them. It would be implemented like this:

At this point we also return the standard network request, which in turn returns a promise. If this promise is resolved, we react with a function that opens our cache with, which returns another promise. When this resolves, it is used to add the resource to the cache. This is extracted, the network response is copied and added to the cache. The copy is saved and the original network response is returned to the browser and thus to the page that made the request.

Why all that? The data stream of the network request and response can only be read once. In order for the network response to be returned to the browser and still be stored in the cache, it must be copied. This allows the original to be sent to the browser and the copy saved in the cache. The original and the copy are therefore only read once.

Finally, there is still the problem of what to do if there is neither a hit in the cache nor an available network connection. Our request to the resource is currently failing. If we provide a workaround, no matter what, users will get an answer:

We decided on an alternative picture at this point, as the only requests that potentially fail will be for new pictures in the gallery. Everything else was taken care of during the installation event.

Update the service worker

If the service worker has already been installed, but a new version will be made available when the page is reloaded, this new worker will be installed in the background, but not yet activated. This only happens when none of the pages using the old worker are loaded any more. As soon as this is the case, the new service worker is activated.

In order for this to happen, the event listener in the new service worker must be adjusted as follows (attention to the new version number):

While this is all happening, the previous version of the service worker is still responsible for managing the requests and the new version is being installed in the background. In this example, the new cache has the name so that the cache with the name is not affected.

When no more pages are using the current version, the new worker is activated and manages the network requests.

Clearing the old cache

An event is still available. It is generally used to deal with things that would have seriously affected the old version if they were done while it was running. This concerns, for example, the deletion of the old caches. The event is also useful for deleting data that is no longer needed and thus preventing it from consuming too much storage space - every browser has a fixed upper limit on the amount of cache space a service worker can use. The browser itself manages the storage space by itself, but it can happen that the cache storage space for a page is deleted, because if the browser deletes data of this type, then either all or none of the data on the page.

Promises that are passed in block further events until they have been processed, so that it can be ensured that all clean-up work is completed before the first network request is made to the new cache.

Developer tools

Chrome has where the activity and disk space of the current service workers can be viewed, and where more details can be found, and where service workers can be started, stopped, and debugged. Future versions will also provide means for network throttling and an offline mode to simulate poor or non-existent network connections.

Firefox has also started developing useful tools related to service workers:

  • Registered service workers can be viewed and updated or removed under about: serviceworkers
  • For test purposes, the HTTPS restriction can be activated by activating the "Activate service worker over HTTP (with open developer tools)" option in the Firefox developer tool options (gear menu)

Specifications

specificationstatuscomment
Service workersWorking draftInitial definition.

Browser compatibility

See also