Chris Muench
Chris Muench

Reputation: 18318

Web Browser Perform Syncing Action While Online In Background

I am looking to synchronize an indexed db in the background for offline access. I want to do this while the application is online and have it run in the background where the user doesn't even know it is running

I looked at backgroundSync with service workers but that appears to be for offline usage.

What I am really looking for is something like a cron task in the browser so I can synchronize data from a remote server to a local in-browser database

Upvotes: 4

Views: 1847

Answers (6)

Alfredo Rahn Linde
Alfredo Rahn Linde

Reputation: 73

Here's a different approach, which fetches the json results from the backend's API, store in localStorage and pass results array to a custom function to render it.

If localStorage if not available in the browser, it fetches the results every time the function is called... so it does when the "force" parameter is set to true.

It also uses cookies for storing the last timestamp when the data was retrieved. The given code is set for a duration of 15 minutes (900,000 milliseconds).

It also assumes that in the json result of the api there's a member called .data where's an array of data to be cached / updated.

It requires jquery for the $.ajax, but I'm sure it can be easily refactored for using fetch, axios, or any other approach:

function getTrans(force=false) {
    var TRANS=undefined
    if(force || window.localStorage===undefined || 
        (window.localStorage!==undefined && localStorage.getItem("TRANS") === null) ||
        $.cookie('L_getTrans')===undefined ||
        ($.cookie('L_getTrans')!==undefined && Date.now()-$.cookie('L_getTrans')>900000)) {
        $.ajax({
            url: 'api/',type: 'post',
            data: {'OP':'getTrans'},
            success: function (result) { 
                TRANS = result.data ?? []
                if(window.localStorage!==undefined) {
                    localStorage.setItem('TRANS', JSON.stringify(TRANS))
                    $.cookie('L_getTrans',Date.now())
                }
                renderTransactionList(TRANS)
            },
            error: function (error) { console.error(error) }
        });
    } else {
        TRANS = JSON.parse(localStorage.getItem('TRANS'))
        renderTransactionList(TRANS)
    }
}

Hope it helps some of you, or even amuse.

Upvotes: 1

lossleader
lossleader

Reputation: 13495

Service Worker Push Notifications

Given your reference to cron, it sounds like you want to sync a remote client at times when they are not necessarily visiting your site. Service Workers are the correct answer for running an operation in ~1 context irregardless of how many tabs the client might have open, and more specifically service workers with Push Notifications are necessary if the client might have no tabs open to the origin site at the time the sync should occur.

Resources to setup web push notifications:

There are many guides for setting up push notifications, i.e.:

which link to the test push services like:

To test sending the push before you have configured your own server to send pushes.

Once you have your test service worker for a basic push notification, you can modify the service worker's push handler to call back to your site and do the necessary DB sync.

Example that triggers a sync into an IndexedDB via a push

Here is a pouchdb example from a few years back that was used to show pouchdb could use its plugins for http and IndexedDB from within the push handler:

 self.addEventListener('push', (event) => {
  if (event.data) {
    let pushData = event.data.json();
    if (pushData.type === 'couchDBChange') {
      logDebug(`Got couchDBChange pushed, attempting to sync to: ${pushData.seq}`);
      event.waitUntil(
        remoteSync(pushData.seq).then((resp) => {
          logDebug(`Response from sync ${JSON.stringify(resp, null, 2)}`);
        })
      );
    } else {
      logDebug('Unknown push event has data and here it is: ', pushData);
    }
  }
});

Here:

  • Pouchdb inside a service worker is receiving only a reference to a sync point in the push itself.
  • It now has a background browser context with an origin of the server that registered the service worker.
  • Therefore, it can use its http sync wrapper to contact the db provided over http by the origin server.
  • This is used to sync its contents which are stored in IndexedDB via its wrapper for IndexedDB

So:

  • Awaking on a push and contacting the server over http to sync to a reference can be done by a service worker to update an IndexedDB implementation as long as the client agreed to receive pushes and has a browser with internet connectivity.
  • A client that does not agree to pushes, but has service workers can also centralize/background sync operations on the service worker when tabs are visiting the site either with messages or chosen URLs to represent a cached sync results. (For a single tab SPA visitor, performance benefits as far as backgrounding are similar to web workers DB performance) but pushes, messages and fetches from the origin are all being brought together in one context which can then provide synchronization to prevent repeated work.

Safari appears to have fixed their IndexedDB in service workers bug, but some browsers may still be a bit unreliable or have unfortunate edge cases. (For example, hitting DB quotas should be tested carefully as they can cause problems when a quota is hit while in an unexpected context.)

Still, this is the only reliable way to get multiple browsers to call back and perform a sync to your server without building custom extensions, etc for each vendor separately.

Upvotes: 0

Srsole
Srsole

Reputation: 101

Maybe if you try with a socket(PHP) and a setTimeout(JS)

Let me explain to you:

When you enter your page, it uses the indexDB, and at the same moment starts a setTimeout, for exemple every 30 sec., and also tries to comunicate with the socket. If this action is successful, the web page synchronizes with the indexDB.

I don't know if you understand me.

My English is very bad. I'm sorry.

Upvotes: 0

Hamit YILDIRIM
Hamit YILDIRIM

Reputation: 4539

I looked at backgroundSync with service workers but that appears to be for offline usage.

No it is not just for offline usage! Also Answer about PWA and service workers also right!

A solution in your case:

You can use navigator.onLine to check the internet connection like that

window.addEventListener('offline', function() {
    alert('You have lost internet access!');
});

If the user loses their internet connection, they'll see an alert. Next we'll add an event listener for watching for the user to come back online.

window.addEventListener('online', function() {
    if(!navigator.serviceWorker && !window.SyncManager) {
        fetchData().then(function(response) {
            if(response.length > 0) {
                return sendData();
            }
        });
    }
});

A good pattern to detection

if(registration.sync) {
    registration.sync.register('example-sync')
    .catch(function(err) {
        return err;
    })
} else {
    if(navigator.onLine) {
        requestSync();
    } else {
        alert("You are offline! When your internet returns, we'll finish up your request.");
    }
}

Note: Maybe you need to limit your application activity while offline

I'm trying to send (push to db) data with fetch api below, but here things like HTML5 Apex or Socket or CacheAPI can also be used.

requestSync() {
    navigator.serviceWorker.ready.then(swRegistration => swRegistration.sync.register('todo_updated'));
  }

Upvotes: 0

hugovicfortman
hugovicfortman

Reputation: 201

I believe what you're going for is a Progressive Web Application (PWA).

To build on Claudio's answer, performing background fetches are best done with a web worker. Web workers are typically stateless, and you would need to adapt your project to note what data was loaded last. However, using History API and (lazy) loading other page contents via JavaScript means that the user wouldn't have to exit the page.

A service worker would be able to monitor when your application is online or offline, and can call methods to pause or continue downloads to the indexed db.

As a side note, it is advisable to load only what is needed by your users, as excessive background loading may offend some users.

Further Reading.

Upvotes: 0

Claudio
Claudio

Reputation: 3095

For your purpose you probably need webworker instead of service worker.

While service worker acts as a proxy for connections, web worker can be more generic.

https://www.quora.com/Whats-the-difference-between-service-workers-and-web-workers-in-JavaScript

Is has some limitation interacting with browser objects but http connections and indexed db are allowed.

Pay particular attention to browser’s cache during development: even cltr + F5 does not reload web worker code.

Force reload/prevent cache of Web Workers

Upvotes: 0

Related Questions