Search code examples
indexeddbservice-workerbackground-sync

Service Worker Background Sync - On rejecting promise - there are no retries from the waitUntil?


I am creating a offline first blogging platform which utilises IndexedDB and service workers. The idea here is if the user is offline and trying to send a post - send it in the background. This project is for my dissertation, I have only been looking at promises for a week or so - apologies if it is a simple error!

From my action in react/redux, i'm sending the sync event through successfully.

Below is the code for my sync event handler

self.addEventListener('sync', function(event) {
  if (event.tag == 'send_post') {
    //const URL
    console.log('sync from SW - send post');
    //this should try again if promise is rejected
    event.waitUntil(
      openDatabase('Outbox').then( (db) => {
        return databaseGet('posts',db).then( (posts) => {
          return sendAllFromOutbox(posts)
        } )
      } )

    );
  }
});

Below is openDatabase - (IndexedDB)

    function openDatabase(name) {
      return new Promise(function(resolve, reject) {
        var version = 10;
        var request = indexedDB.open(name, version);
        var db;
        request.onupgradeneeded = function(e) {
          db = e.target.result;
          e.target.transaction.onerror = reject;
        };
        request.onsuccess = function(e) {
          db = e.target.result;
          console.log('OPENED DATABASE');
          resolve(db);
        };
        request.onerror = reject;
      });
    }

Below is databaseGet

function databaseGet(type,db) {
  return new Promise(function(resolve, reject) {
    var transaction = db.transaction([type], 'readonly');
    var store = transaction.objectStore(type);
    var request = store.getAll();
    request.onsuccess = function(e) {
      var result = e.target.result;
      resolve(result);
    };
    request.onerror = reject;
  });
}

And finally, below is sendAllFromOutbox

function sendAllFromOutbox(posts) {
  return fetch('https://stirapi.herokuapp.com/sendPost', {
    headers: {'Content-Type': 'application/json'},
    method: "POST",
    body: JSON.stringify(posts)
  })
  .then( (response) => {
    console.log('response from SW sendAllFromOutbox', response);
  } )
  .catch( (err) => {
    console.log('error from SW sendAllFromOutbox',err);
  } )
}

From my understanding, if sendAllFromOutbox fails/rejects - it should get called again. But it doesn't seem to be getting called - and therefore is not being sent in the background.

If you would like to check out my repo - it's here https://github.com/georgecook92/Stir.

Thank you!

George


Solution

  • It's down to the browser to decide when the failed sync event should be retried. Also, it won't retry infinitely, but you know it's the last attempt via syncEvent.lastChance (spec).

    Looking at your code above, databaseGet expects (type,db), but you call it databaseGet('posts'), so that'll throw an error when you try and access properties of db, which is undefined. Chrome's devtools should show this, especially with "break on caught errors".

    The idea here is if the user is offline and trying to send a post - send it in the background

    It's best to use background sync regardless of the user's current state. Sure, navigator.onLine will tell you if the user is definitely offline, but if onLine is true the user still may not have a reliable connection.