閱讀目錄html
一:什麼是後臺同步保證離線功能呢?前端
在咱們作移動端開發也好,仍是作PC端應用也好,咱們常常會碰到填寫表單這樣的功能,若是咱們的表單填寫完成之後,咱們點擊提交,可是這個時候我忽然進入了電梯,或者咱們在高鐵上作這麼一個操做,忽然斷網了,或者說咱們的網絡很差的狀況下,那麼通常的狀況下會一直請求,當咱們的請求超時的時候就會請求失敗,或者說請求異常,最後就會提示咱們網絡異常這些信息,那麼這樣對於用戶體驗來講並非很好,那麼如今咱們來理解下什麼是後臺同步保證離線功能呢?後臺同步離線功能就是說當咱們的網絡很差的時候,咱們點擊提交按鈕時,咱們會保證該應用必定是成功的,不會提示網絡異常這些信息,當網絡鏈接失敗的時候,咱們會在前端頁面顯示一個提示,好比說,正在請求中,請稍微.... 這樣的一個提示,當咱們的網絡恢復正常了,咱們會從新去請求下該接口,那麼咱們這個應用就提示操做成功狀態了。node
後臺同步:它使得咱們可以確保用戶採起的任何操做都能完成,無論用戶的連接狀態如何,甚至當用戶點擊提交後,直接關閉咱們這個應用,再也不回來,而且關閉瀏覽器,後臺同步操做也可以完成。jquery
後臺同步的優勢:webpack
1. 對於用戶而言,可以信任咱們的漸進式web應用能一直工做,這也意味者咱們與傳統的web開發是有區別的,咱們能夠實現原生應用相似的效果。git
2. 對於企業來說,讓用戶在連接失敗的時候,也可以訂火車票,訂閱新聞或發送消息,對於這樣的用戶體驗也會更好。github
二:後臺同步是如何實現的呢?web
後臺同步原理的實質是:它是將操做從頁面上下文中剝離開來,而且在後臺運行。ajax
經過將這些操做放到後臺,它就不會受到單個網頁的影響,即便網頁被關閉,用戶鏈接會斷開,甚至服務器有時候會出現故障,可是隻要咱們電腦上安裝了瀏覽器,後臺同步的操做就不會消失,直到它成功完成爲止。數據庫
1. 註冊一個同步事件
使用後臺同步很簡單,咱們首先要註冊一個同步事件,以下代碼:
navigator.serviceWorker.ready.then(function(registration) { registration.sync.register('send-messages'); });
如上代碼能夠在頁面上運行,它獲取了當前激活的service Worker 的 registration 對象,並註冊了一個叫 send-messages 的sync事件。
如今,咱們就能夠將一個監聽該同步事件的事件監聽器添加到 service worker 中,該事件包含的邏輯將會在service worker 中執行,而不是在頁面上執行的。
以下代碼:
self.addEventListener("sync", function(event) { if (event.tag === "send-messages") { event.waitUntil(function(){ var sent = sendMessages(); if (sent) { return Promise.resolve(); } else { return Promise reject(); } }) } });
2. 理解 SyncManager
咱們上面已經註冊了一個sync事件,而且在service worker中監聽了該sync事件。
那麼全部與sync事件的交互都是經過 SyncManager 來完成的。SyncManager是 service worker 的一個接口。它可讓咱們註冊sync事件,而且咱們能夠獲取已經註冊的sync事件列表。
訪問 SyncManager
咱們能夠經過已經激活的service worker的registration對象來訪問 SyncManager, 在service worker裏面,咱們能夠經過調用navigator.serviceWorker.ready 來訪問當前激活的 service worker 的 registration對象,該方法會返回一個Promise對象,當成功時候咱們能夠拿到service worker的registration對象。
以下代碼:
navigator.serviceWorker.ready.then(function(registration){});
如上代碼,咱們已經得到到了 registration 對象後,無論咱們是在service worker上仍是在頁面上,和SyncManager交互如今都是同樣的。
3. 註冊事件
想要註冊 sync事件,咱們能夠在 SyncManager中調用 register, 傳入一個咱們想要註冊的 sync事件名稱。
好比咱們想註冊一個在service worker中叫 send-message的事件,咱們可使用以下代碼:
self.registration.sync.register("send-message");
若是咱們想在service-worker中想作同樣的事情的話,咱們可使用以下代碼:
navigator.serviceWorker.ready.then(function(registration) { registration.sync.register("send-message"); });
4. 理解Sync事件原理
SyncManager 維護了一個Sync事件標籤列表,SyncManager只知道哪些事件被註冊了,什麼時候被調用,以及如何發送sync事件。
當咱們下面任何一個事件發生的時候,SyncManager會給列表中的每個註冊事件名發送一個sync事件。
1) sync事件註冊後會當即發送。
2)當用戶離線變成在線的時候也會發送。
3)若是還有未完成的事件時,每隔幾分鐘會發送。
在service worker中,咱們發送sync事件,那麼該事件就能夠被監聽到,而且咱們可使用promise進行響應,若是咱們的這個promise完成了,那麼對應的sync註冊會從 SyncManager中刪除,若是promise拒絕了,那麼咱們的sync註冊的事件就會保留在SyncManager中,而且每隔幾分鐘會在下一個同步機會進行重試。
5. 理解 sync事件中的事件名稱
sync事件中的事件名稱是惟一的。若是在SyncManager中使用一個已經被註冊的事件名稱繼續來註冊的話,那麼SyncManager 會忽略它,好比說:咱們正在構建一個郵件服務,每當用戶發送消息的時候,咱們能夠把消息保存到 indexedDB的發件箱中,而且註冊一個send-email-message這樣的後臺同步事件,那麼咱們的service worker能夠包含一個事件監聽器進行監聽,它會遍歷indexedDB發件箱中的每一條消息,嘗試發送他們,而且當發送成功後,將會從 indexedDB隊列中刪除它,若是咱們當中有某條消息並無發送成功的話,那麼該sync事件就會被拒絕,SyncManager將會在稍後再次發送該事件,可是該事件是咱們上次事件中發送失敗的那個事件。使用這種設置,咱們永遠不須要檢查發件箱中是否存在消息,只要有未發送的電子郵件,sync事件就會保持註冊,而且嘗試清空咱們發的發件箱。
5. 理解獲取已經註冊的sync事件列表
咱們使用SyncManager的getTags()方法,就能夠獲得完整的已註冊同步事件列表。該getTags()方法也會返回一個Promise對象,該promise對象完成後,會得到一個sync註冊事件名稱的數組。
在service-worker中,咱們能夠註冊一個叫 hello-world的sync事件,而後將當前註冊的完整事件列表打印在控制檯中中;以下代碼:
self.registration.sync.register("hello-world").then(function() { return self.registration.sync.getTags(); }).then(function(tags) { console.log(tags); });
在咱們的service worker中,咱們首先經過使用 ready 獲取 registration對象,也能夠獲取同樣的結果,以下代碼所示:
navigator.serviceWorker.ready.then(function(registration){ registration.sync.register('hello-world').then(function() { return registration.sync.getTags(); }).then(function(tags) { console.log(tags); }); });
6. 最後一次發生sync事件
在有些狀況下,SyncManager能夠會判斷出嘗試發送的sync事件已經屢次失敗,當發生這種狀況的時候,SyncManager將會發送最後一次事件,給咱們最後一次響應的機會,咱們能夠經過sync事件的lastChance屬性來判斷何時會發生這種狀況,以下代碼:
self.addEventListener("sync", event => { if (event.tag === "hello-world") { event.waitUntil( // 調用 addReservation方法 addReservation().then(function(){ return Promise.resolve(); }).catch(function(error) { if (event.lastChance) { return removeReservation(); } else { return Promise.reject(); } }) ) } })
三:如何給sync事件傳遞數據?
在頁面接口交互中,咱們可能須要傳遞一些參數進去,好比說,發生一個消息的接口中,可能咱們須要把消息文本發送過去,一個爲帖子點讚的接口,咱們須要把帖子id的參數傳遞過去。可是當咱們註冊sync事件時,咱們目前來看,咱們以前只能傳遞事件名稱,可是咱們如何把一些對應的參數也傳遞給sync中的事件當中呢?
1. 在indexedDB中維護操做隊列
要想把一些參數傳遞過去,咱們能夠把這些參數先保存到咱們的indexedDB中,而後,咱們在service worker中的sync事件代碼咱們能夠迭代該對象存儲,而且在每一個條目上執行所需的操做,一旦操做成功了,咱們就能夠把該實體從對象存儲中刪除掉。
如今咱們來作個demo,咱們如今須要把每一條消息能夠添加到 message-queue對象存儲中,而後咱們註冊一個 send-message 後臺同步事件來處理,該事件會遍歷 message-queue對象中的全部消息,依次將他們發送到網絡中,若是當全部消息都發送成功的話,咱們會依次將消息隊列中的數據刪除,所以對象存儲就爲空了。可是若是有任何一條消息沒有發送成功的話,就會向sync事件返回一個拒絕的promsie,SyncManager在稍後一段時間內會再次運行該sync事件。
若是咱們以前使用以下代碼,來請求一個接口,以下代碼所示:
var sendMessage = function(subject, message) { fetch('/new-message', { method: 'post', body: JSON.stringify({ subject: subject, msg: message }) }) };
如今咱們使用service worker,須要把代碼改爲以下所示:
var triggerMessageQueueUpdate = function() { navigator.serviceWorker.ready.then(function(registration) { registration.sync.register("message-queue-sync"); }); }; var sendMessage = function(subject, message) { addToObjectStore("message-queue", { subject: subject, msg: message }); triggerMessageQueueUpdate(); };
而後咱們須要在service worker中監聽sync事件代碼以下:
self.addEventListener("sync", function(event) { if (event.target === 'message-queue-sync') { event.waitUntil(function() { return getAllMessages().then(function(messages) { return Promise.all( messages.map(function(message) { return fetch('/new-message', { method: 'post', body: JSON.stringify({ subject: subject, msg: message }) }).then(function(){ return deleteMessageFromQueue(message); }) }) ) }) }) } });
如上改寫後的代碼,首先咱們會調用 addToObjectStore 這個方法來把消息保存到咱們的key爲 'message-queue' 當中,而後調用 triggerMessageQueueUpdate 這個方法,使用sync註冊message-queue-sync這個事件,而且咱們使用sync監聽了該事件名稱,而後咱們使用了 getAllMessages 方法獲取indexedDB的消息隊列中的全部消息,而且最終返回了一個promise給sync事件,在該代碼中,咱們使用了Promise.all方法,在該方法內部,只有咱們的消息發送成功後,咱們纔會使用 deleteMessageFromQueue方法來刪除該消息,在咱們的消息數組中,咱們使用了map()方法遍歷爲每條消息發送一個promise對象.
2. 在indexedDB中維護請求隊列
有時候在咱們的項目中,咱們須要實現本地存儲架構來對對象狀態進行跟蹤,若是頁面上有多個ajax請求的話,咱們可使用service worker 在indexedDB中來維護請求隊列,咱們能夠將網絡上的每一個請求存儲到indexedDB中,而後該方法會註冊一個sync事件,該事件會遍歷對象存儲中全部請求,並依次執行。
好比項目中有以下代碼:
var sendMessage = function(subject, message) { fetch('/new-message', { method: 'post', body: JSON.stringify({ subject: subject, msg: message }) }) }; var getRequest = function(id) { fetch('/like-post?id='+id); };
如上兩個請求,咱們使用service worker 換成以下代碼:
var triggerRequestQueueSync = function() { navigator.serviceWorker.ready.then(function(registration){ registration.sync.register("request-queue"); }); }; var sendMessage = function(subject, message) { addToObjectStore("request-queue", { url: '/new-message', method: 'post', body: JSON.stringify({ subject: subject, msg: message }) }); triggerRequestQueueSync(); }; var getRequest = function(id) { addToObjectStore('request-queue', { url: '/like-post?id=' + id, method: 'get' }); triggerRequestQueueSync(); };
如上代碼,咱們將全部的網絡請求替換成如上的代碼,將表明請求對象存儲到 request-queue的對象存儲中,這個存儲中每一個對象表明一個網絡請求,接下來咱們須要添加一個sync事件監聽器到service worker中,它負責遍歷 request-queue的全部請求,依次會發起一個網絡請求,發送成功後,依次從對象存儲中刪除。以下代碼所示:
self.addEventListener("sync", function(event) { if (event.tag === "request-queue") { event.waitUntil(function(){ return getAllObjectsFrom("request-queue").then(function(requests) { return Promise.all( requests.map(function(req) { return fetch(req.url, { method: req.method, body: req.body }).then(function() { return deleteRequestFromQueue(req); // 返回一個promise }) }) ) }); }) } });
如上代碼,若是一個請求發送成功了,就會從indexedDB中隊列中刪除掉,失敗的請求會保留到隊列中,而且返回被拒絕的promise,那麼失敗的promise會在請求隊列的下一次sync事件中再次迭代。
3. 使用一種更簡單的方式傳遞數據給事件名稱
當咱們須要傳遞一個簡單的數據給sync函數時候,咱們就不須要使用上面的indexedDB來存儲數據,而後再service worker中依次遍歷拿到該對象了,咱們可使用一種更簡單的方式來解決如上的問題。咱們以前的代碼是這樣的:
var likePost = function(postId) { fetch("/like-post?id="+postId); };
咱們能夠在service worker中使用以下代碼來進行改造,以下代碼所示:
var likePost = function(postId) { navigator.serviceWorker.ready.then(function(registration){ registration.sync.register("like-post-"+postId); }); };
咱們使用sync事件來監聽上面的函數,代碼以下:
self.addEventListener("sync", function(event) { if (event.tag.startsWith("like-post-")) { event.waitUntil(function(){ var postId = event.tag.slice(10); return fetch('/like-post?id='+postId); }) } });
四:在咱們的項目中添加後臺同步功能
在咱們項目中添加後臺同步功能以前,咱們仍是來看下咱們項目中的整個目錄架構以下所示:
|----- service-worker-demo6 | |--- node_modules # 項目依賴的包 | |--- public # 存放靜態資源文件 | | |--- js | | | |--- main.js # js 的入口文件 | | | |--- store.js # indexedDB存儲 | | | |--- myAccount.js | | |--- styles | | |--- images | | |--- index.html # html 文件 | |--- package.json | |--- webpack.config.js | |--- sw.js
該篇文章是在上篇文章基礎之上繼續擴展的,若是想要看上篇文章,請點擊這裏
咱們首先來看下咱們 public/index.html 代碼以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>service worker 實列</title> </head> <body> <div id="app">222226666</div> <img src="/public/images/xxx.jpg" /> <div style="cursor: pointer;color:red;font-size:18px;padding:5px;border:1px solid #333;" id="submit">點擊我新增</div> <div style="cursor: pointer;color:red;font-size:18px;padding:5px;border:1px solid #333;" id="update">點擊我修改</div> </body> </html>
public/js/myAccout.js 代碼以下(該代碼的做用最主要是作頁面業務邏輯代碼)
import $ from 'jquery'; $(function() { function renderHTMLFunc(obj) { console.log(obj); } function updateDisplay(d) { console.log(d); }; var addStore = function(id, name, age) { var obj = { id: id, name: name, age: age }; addToObjectStore("store", obj); renderHTMLFunc(obj); $.getJSON("http://localhost:8081/public/json/index.json", obj, function(data) { updateDisplay(data); }); }; $("#submit").click(function(e) { addStore(3, 'longen1', '111'); }); $("#update").click(function(e) { $.getJSON("http://localhost:8081/public/json/index.json", {id: 1}, function(data) { updateInObjectStore("store", 1, data); updateDisplay(data); }); }); });
如上myAccout.js代碼,當我點擊 id 爲 "submit" 的div元素的時候(咱們能夠假設這是一個form表單提交,這邊爲了演示這個做用懶得使用form表單來演示),當我點擊該div元素的時候,咱們的addStore函數會被調用,這個函數內部會調用 addToObjectStore()這個方法,這個函數會添加一個store對象的存儲,它會把該對象添加到IndexedDB的store對象中,添加完成之後,咱們會調用renderHTMLFunc() 這個方法來渲染咱們的html頁面,而且以後咱們會發起一個ajax請求。若是咱們的網絡一直是能夠用的話,那麼咱們就不須要作任何處理操做,可是若是咱們的網絡鏈接失敗的狀況下,咱們調用了 addStore 方法,那麼咱們新的數據會被添加到indexedDB中,而且會調用renderHTMLFunc方法來渲染咱們的頁面,可是後面的ajax請求就會調用失敗。頁面雖然更新了,indexedDB數據也保存到本地了,可是咱們的服務器徹底不知情,所以在這種狀況下,咱們須要使用service worker中的sync事件來解決這個問題。
咱們要完成以下步驟:
1)在addStore函數添加代碼,檢查瀏覽器是否支持後臺同步。若是支持,則註冊一個 sync-store 同步事件,不然的話,便使用長規的ajax調用。
2)在store.js 中,添加到indexedDB代碼,須要把狀態改成 sending(發送中),在發送請求到服務器以前,這就是用戶看到的狀態,操做成功後,服務器會返回新的狀態。
3)咱們會向service worker添加一個事件監聽器,用來監聽sync事件,若是咱們檢測到sync的事件名稱是 sync-store ,事件監聽器就會遍歷每個處於 sending 狀態的預訂,而且嘗試發送給服務器。成功添加到服務器以後,indexedDB中的狀態就會被修改爲爲新的狀態,若是任何服務器請求失敗的話,那麼整個sync事件就會被拒絕,瀏覽器就會嘗試在隨後再運行這個事件了。
所以咱們如今的第一步是在 addStore函數中,添加瀏覽器是否支持同步功能,若是支持的話,就會註冊一個sync事件。以下代碼(在myAccount.js 代碼修改):
var addStore = function(id, name, age) { var obj = { id: id, name: name, age: age }; addToObjectStore("store", obj); renderHTMLFunc(obj); // 先判斷瀏覽器支付支持sync事件 if ("serviceWorker" in navigator && "SyncManager" in window) { navigator.serviceWorker.ready.then(function(registration) { registration.sync.register("sync-store") }); } else { $.getJSON("http://localhost:8081/public/json/index.json", obj, function(data) { updateDisplay(data); }); } };
所以咱們的 public/js/myAccount.js 全部的代碼以下:
import $ from 'jquery'; $(function() { function renderHTMLFunc(obj) { console.log(obj); } function updateDisplay(d) { console.log(d); }; var addStore = function(id, name, age) { var obj = { id: id, name: name, age: age }; addToObjectStore("store", obj); renderHTMLFunc(obj); // 先判斷瀏覽器支付支持sync事件 if ("serviceWorker" in navigator && "SyncManager" in window) { navigator.serviceWorker.ready.then(function(registration) { registration.sync.register("sync-store").then(function() { console.log("後臺同步已觸發"); }).catch(function(err){ console.log('後臺同步觸發失敗', err); }) }); } else { $.getJSON("http://localhost:8081/public/json/index.json", obj, function(data) { updateDisplay(data); }); } }; $("#submit").click(function(e) { addStore(3, 'longen1', '111'); }); $("#update").click(function(e) { $.getJSON("http://localhost:8081/public/json/index.json", {id: 1}, function(data) { updateInObjectStore("store", 1, data); updateDisplay(data); }); }); });
2)其次咱們須要在 public/js/store.js 中openDataBase方法中的 result.onupgradeneeded 函數代碼改爲以下(固然要觸發該函數,咱們須要升級咱們的版本號,即把 var DB_VERSION = 2; 把以前的 DB_VERSION 值爲1 改爲2):
var DB_VERSION = 2; var DB_NAME = 'store-data2'; // 監聽當前版本號被升級的時候觸發該函數 result.onupgradeneeded = function(event) { var db = event.target.result; var upgradeTransaction = event.target.transaction; var reservationsStore; /* 是否包含該對象倉庫名(或叫表名)。若是不包含就建立一個。 該對象中的 keyPath屬性id爲主鍵 */ if (!db.objectStoreNames.contains('store')) { reservationsStore = db.createObjectStore("store", { keyPath: "id", autoIncrement: true }); } else { reservationsStore = upgradeTransaction.objectStore("store"); } if (!reservationsStore.indexNames.contains("idx_status")) { reservationsStore.createIndex("idx_status", "status", {unique: false}); } }
如上代碼,咱們在建立 store對象以前,咱們會先判斷該對象是否存在,若是不存在的話,咱們會建立該對象,不然的話,咱們就經過調用 event.target.transaction.objectStore("store")將得到更新事件中的事務,而且從事務中獲取store對象存儲的引用。
最後咱們確認咱們的store對象存儲是否已經有 idx_status 這個,若是不存在的話,若是不存在的話,咱們就建立該索引。
3)如今咱們須要修改咱們的 public/js/store.js 中的getStore函數了,咱們在該函數內部使用這個新的索引,就能夠獲取到該某個請求中的某個狀態了,所以咱們要對 咱們的 getStore函數進行修改,讓其支持接收兩個可選的參數,索引名稱,以及傳遞給該索引的值。以下代碼的修改:
var getStore = function (indexName, indexValue) { return new Promise(function(resolve, reject) { openDataBase().then(function(db) { var objectStore = openObjectStore(db, 'store'); var datas = []; var cursor; if (indexName && indexValue) { cursor = objectStore.index(indexName).openCursor(indexValue); } else { cursor = objectStore.openCursor(); } cursor.onsuccess = function(event) { var cursor = event.target.result; if (cursor) { datas.push(cursor.value); cursor.continue(); } else { if (datas.length > 0) { resolve(datas); } else { getDataFromServer().then(function(d) { openDataBase().then(function(db) { var objectStore = openObjectStore(db, "store", "readwrite"); for (let i = 0; i < datas.length; i++) { objectStore.add(datas[i]); } resolve(datas); }); }); } } } }).catch(function() { getDataFromServer().then(function(datas) { resolve(datas); }); }); }); };
如上代碼,咱們對getStore函數接受了兩個可選的新參數(indexName和indexValue)。其次,若是咱們的函數接收了這些參數的話,就使用參數在特定的索引(indexName)上打開流標,而後打開特定值(indexValue)的流標,會把結果限定在指定的範圍內。若是沒有傳遞這些參數的話,它會向之前那樣運行。
作出這兩個地方的修改,咱們的函數能夠返回全部的結果,也能夠返回結果中的一個子集,以下代碼所示:
getStore().then(function(reservations){ // reservations 包含了全部的數據 }); getStore("idx_status", "Sending").then(function() { // reservations 變量僅僅包含了狀態爲 "Sending" 的數據 });
4)如今咱們須要在咱們的 sw.js 中添加後臺同步的事件監聽器到 service worker中了。
首先咱們在咱們的sw.js中引入 store.js ,代碼以下所示:
importScripts("/public/js/store.js");
而後在咱們sw.js的底部,添加以下代碼:
var createStoreUrl = function(storeDetails) { var storeUrl = new URL("http://localhost:8081/public/json/index.json"); Object.keys(storeDetails).forEach(function(key) { storeUrl.searchParams.append(key, storeDetails[key]); }); return storeUrl; }; var syncStores = function() { return getStore("idx_status", "Sending").then(function(reservations) { return Promise.all( reservations.map(function(reservation){ var reservationUrl = createStoreUrl(reservation); return fetch(reservationUrl); }) ) }); }; self.addEventListener("sync", function(event) { if (event.tag === "sync-store") { event.waitUntil(syncStores()); } });
如上代碼,咱們使用 self.addEventListener 爲sync事件添加一個新的事件監聽器,這個事件監聽器會響應事件名稱爲 "sync-store" 的事件,而後使用 waitUntil方法等待 syncStores()函數返回的promise,會根據該promise的完成或拒絕來判斷sync事件是完成仍是拒絕,若是是完成的話,那麼 sync-store的sync事件就會從SyncManager中刪除,若是promise是拒絕的話,那麼咱們的SyncManager會保持 sync事件的註冊,而且在隨後會再次觸發該事件。
syncStores() 會遍歷IndexedDB中每個標記爲"Sending" 狀態的數據,嘗試會再次發送到服務器,而且返回一個promise,只有當promise發送成功了的話,那麼就會完成狀態。
而後在咱們的getStore函數中,能夠獲取全部處於 Sending 狀態的數據,該函數也返回了一個promise對象,該promise會決定整個syncStores函數的結果。要實現這點,咱們使用了Promise.all()傳入了一個promise數組,咱們拿到該數據對象數組後,會經過Array.map()方法將數組的元素轉化爲 promise,咱們使用 map對每一個元素進行迭代,建立一個fetch請求發送到服務器來建立這個請求,fetch也會返回一個promise。
createStoreUrl 函數使用URL接口建立了一個新的URL對象,這個對象表示的是fetch請求接口,使用這種方式更優雅的建立帶有查詢字符串的URL,好比以下代碼會打印帶參數的url。
console.log(createStoreUrl({'name': 'kongzhi', 'age': 30}));
那麼打印的 結構就是:http://localhost:8081/public/json/index.json?name=kongzhi&age=30; 這樣的了。
完成上面的代碼後,咱們來打開咱們的應用 http://localhost:8081/ 刷新下,而後咱們把咱們的網絡斷開,而後再點擊 "點擊我新增"這個文字,就會在控制檯上打印以下信息了;以下圖所示:
而後咱們打開咱們的網絡,沒過一下子,就能夠看到咱們的請求會自動請求一次,以下圖所示:
請求成功後,咱們就能夠把頁面的消息 "請求加載中, 請稍後..." 這幾個字 能夠改爲 "請求成功了..." 這樣的提示了。
如上代碼後,當咱們的網絡恢復完成後,咱們會從新發ajax請求,請求完成後,可能會有新的請求狀態數據,所以咱們如今最後一步是須要更新咱們的indexedDB數據庫了,以便顯示最新的消息給咱們的用戶,而且咱們要更新咱們的數據狀態,等咱們下一次 sync-store 事件註冊的時候,不會從新發送。所以咱們須要改變syncStores函數代碼:
在更新以前,咱們以前的代碼是以下這樣的:
var syncStores = function() { return getStore("idx_status", "Sending").then(function(reservations) { console.log(reservations); return Promise.all( reservations.map(function(reservation){ var reservationUrl = createStoreUrl(reservation); return fetch(reservationUrl); }) ) }); };
更新以後的代碼以下所示:
var syncStores = function() { return getStore("idx_status", "Sending").then(function(reservations) { console.log(reservations); return Promise.all( reservations.map(function(reservation){ var reservationUrl = createStoreUrl(reservation); return fetch(reservationUrl).then(function(response) { return response.json(); }).then(function(newResponse) { return updateInObjectStore("store", 1, newResponse); }) }) ) }); };