
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.knowledge.knowledge); // 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.incorporates("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(process){ // 14
let transaction = db.transaction("duties", "readwrite");
let tasksObjectStore = transaction.objectStore("duties");
let addRequest = tasksObjectStore.add(process);
addRequest.onsuccess = operate(occasion){ console.log("Activity 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 process = cursor.worth; // 18
fetch(URL + 'todos/add', // a
{ technique: 'POST',
headers: { 'Content material-Sort': 'utility/json' },
physique: JSON.stringify({ "process" : process })
}).then((serverResponse) => {
console.log("Activity 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 discuss what is going on 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 identify so we will hear for messages.
- Right here, we’re anticipating
task-channelmessage occasions. In responding to those occasions, we do two issues: - Name
persistTask()to avoid wasting the brand new process toIndexedDB. - Register a brand new
syncoccasion. That is what invokes the particular functionality for retrying requests intelligently. The sync handler permits us to specify a promise that it’ll retry when the community is offered, and implements a again off technique and give-up situations. - With that performed, we create a reference for our database object.
- Get hold of a “request” for the deal with on our database. Every part on
IndexedDBis dealt with asynchronously. (For a superb overview ofIndexedDB, I like to recommend this sequence.) - The
onupgradeneededoccasion 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 worlddbobject. - If the duties assortment just isn’t current, we create the duties assortment.
- If the database was efficiently created, we reserve it to our
dbobject. - 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 process worth within the duties assortment. - Our sync occasion. That is known as by the published occasion (5). We verify for the
occasion.tagsubject beingtask-syncso we all know it’s our task-syncing occasion. occasion.waitUntil()permits us to inform theserviceWorkerthat we aren’t performed till thePromiseinside it completes. As a result of we’re in a sync occasion, this has particular that means. Specifically, if ourPromisefails, the syncing algorithm will hold making an attempt. Additionally, do not forget that if the community is unavailable, it’s going to 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
- Inside the database
onsuccesscallback, we receive a cursor and use it to seize the duty we saved. (We’re leveraging our wrappingPromiseto take care of nested asynchronous calls.) - Now we have now a variable with the worth of our broadcast process in it. With that in hand:
- We situation a brand new
fetchrequest to ourexpressJS /todos/addendpoint. - 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(). This may reject the containing promise, letting the Sync API know the request should be retried.
- We situation a brand new
- The
deleteTasks()helper technique deletes all of the duties within the database. (This can be a simplified instance that assumes onedutiescreation at a time.)
Clearly, there’s a lot to this, however the reward is having the ability to effortlessly retry requests within the background each time our community is spotty. Bear in mind, we’re getting this within the browser, throughout all types of units, cellular and in any other case.
Testing the PWA instance
For those who run the PWA now and create a to-do, it’ll be despatched to the again finish and saved. The fascinating take a look at is to open devtools (F12) and disable the community. You could find the “Offline” possibility within the “throttling” menu of the community tab like so:
