const URL = "https://8014-35-223-70-178.ngrok-free.app/"; // 1
const taskChannel = new BroadcastChannel('task-channel'); // 2
taskChannel.onmessage = occasion => { // 3
persistTask(occasion.information.information); // 4
registration.sync.register('task-sync'); // 5
};
let db = null; // 6
let request = indexedDB.open("TaskDB", 1); // 7
request.onupgradeneeded = operate(occasion) { // 8
db = occasion.goal.end result; // 9
if (!db.objectStoreNames.comprises("duties")) { // 10
let tasksObjectStore = db.createObjectStore("duties", { autoIncrement: true }); // 11
}
};
request.onsuccess = operate(occasion) { db = occasion.goal.end result; }; // 12
request.onerror = operate(occasion) { console.log("Error in db: " + occasion); }; // 13
persistTask = operate(activity){ // 14
let transaction = db.transaction("duties", "readwrite");
let tasksObjectStore = transaction.objectStore("duties");
let addRequest = tasksObjectStore.add(activity);
addRequest.onsuccess = operate(occasion){ console.log("Process added to DB"); };
addRequest.onerror = operate(occasion) { console.log(“Error: “ + occasion); };
}
self.addEventListener('sync', async operate(occasion) { // 15
if (occasion.tag == 'task-sync') {
occasion.waitUntil(new Promise((res, rej) => { // 16
let transaction = db.transaction("duties", "readwrite");
let tasksObjectStore = transaction.objectStore("duties");
let cursorRequest = tasksObjectStore.openCursor();
cursorRequest.onsuccess = operate(occasion) { // 17
let cursor = occasion.goal.end result;
if (cursor) {
let activity = cursor.worth; // 18
fetch(URL + 'todos/add', // a
{ methodology: 'POST',
headers: { 'Content material-Sort': 'software/json' },
physique: JSON.stringify({ "activity" : activity })
}).then((serverResponse) => {
console.log("Process saved to backend.");
deleteTasks(); // b
res(); // b
}).catch((err) => {
console.log("ERROR: " + err);
rej(); //c
})
}
}
}))
}
})
async operate deleteTasks() { // 19
const transaction = db.transaction("duties", "readwrite");
const tasksObjectStore = transaction.objectStore("duties");
tasksObjectStore.clear();
await transaction.full;
}
Now let’s speak about what is occurring on this code.
- We have to route our requests by means of the identical safe tunnel we created with
ngrok
, so we save the URL right here. - Create the published channel with the identical title so we will pay attention for messages.
- Right here, we’re waiting for
task-channel
message occasions. In responding to those occasions, we do two issues: - Name
persistTask()
to avoid wasting the brand new activity toIndexedDB
. - Register a brand new
sync
occasion. That is what invokes the particular functionality for retrying requests intelligently. The sync handler permits us to specify a promise that it’s going to retry when the community is on the market, and implements a again off technique and give-up situations. - With that completed, we create a reference for our database object.
- Acquire a “request” for the deal with on our database. The whole lot on
IndexedDB
is dealt with asynchronously. (For a wonderful overview ofIndexedDB
, I like to recommend this collection.) - The
onupgradeneeded
occasion fires if we’re accessing a brand new or up-versioned database. - Inside
onupgradeneeded
, we get a deal with on the database itself, with our internationaldb
object. - If the duties assortment isn’t current, we create the duties assortment.
- If the database was efficiently created, we put it aside to our
db
object. - Log the error if the database creation failed.
- The
persistTask()
operate known as by the add-task broadcast occasion (4). This merely places the brand new activity worth within the duties assortment. - Our sync occasion. That is known as by the published occasion (5). We test for the
occasion.tag
discipline beingtask-sync
so we all know it’s our task-syncing occasion. occasion.waitUntil()
permits us to inform theserviceWorker
that we aren’t completed till thePromise
inside it completes. As a result of we’re in a sync occasion, this has particular which means. Particularly, if ourPromise
fails, the syncing algorithm will preserve making an attempt. Additionally, do not forget that if the community is unavailable, it’ll wait till it turns into obtainable.- We outline a brand new
Promise
, and inside it we start by opening a connection to the database.
- We outline a brand new
- Throughout the database
onsuccess
callback, we get hold of a cursor and use it to seize the duty we saved. (We’re leveraging our wrappingPromise
to take care of nested asynchronous calls.) - Now we’ve got a variable with the worth of our broadcast activity in it. With that in hand:
- We problem a brand new
fetch
request to ourexpressJS /todos/add
endpoint. - Discover that if the request succeeds, we delete the duty from the database and name
res()
to resolve our outer promise. - If the request fails, we name
rej()
. It will reject the containing promise, letting the Sync API know the request have to be retried.
- We problem a brand new
- The
deleteTasks()
helper methodology deletes all of the duties within the database. (It is a simplified instance that assumes oneduties
creation at a time.)
Clearly, there’s a lot to this, however the reward is having the ability to effortlessly retry requests within the background at any time when our community is spotty. Keep in mind, we’re getting this within the browser, throughout every kind of units, cell and in any other case.
Testing the PWA instance
In the event you run the PWA now and create a to-do, it’ll be despatched to the again finish and saved. The fascinating check is to open devtools (F12) and disable the community. You will discover the “Offline” choice within the “throttling” menu of the community tab like so: