摘要:綜上,對進行一定的封裝,來簡化編碼操作。化的嘗試對于這種帶大量回調的,使用進行異步化封裝是個好主意。因此包括在內的所有異步方法都會強制中止當前事務。這就決定了一個事務內部的所有操作必須是同步完成的。目前只實現(xiàn)了和,其他的有待下一步工作。
前言
本文是介紹我在編寫indexedDB封裝庫中誕生的一個副產品——如何讓indexedDB在支持鏈式調用的同時,保持對事務的支持。
項目地址:https://github.com/woodensail/indexedDB
2015/11/12 注:這篇文章的思路有問題,大家看看了解一下就行,不要這么干。更好的做法已經寫在了下一篇中。大家可以去看一下,順便幫忙點個推薦或者收藏一個。
地址:http://segmentfault.com/a/1190000003984871
var tx = db.transaction("info", "readonly"); var store = tx.objectStore("info"); store.get("id").onsuccess = function (e) { console.log(e.target.result); };
上面這段代碼中,開啟了一個事務,并從名為info的store中取得了key為id的記錄,并打印在控制臺。
其中打印的部分寫在了onsuccess回調中,如果我們希望把取出的id加1然后返回就需要這樣:
// 方案1 var tx = db.transaction("info", "readwrite"); var store = tx.objectStore("info"); store.get("id").onsuccess = function (e) { store.put({key:"id",value:e.target.result.value + 1}).onsuccess = function (e) { …… }; }; // 方案2 var tx = db.transaction("info", "readwrite"); var store = tx.objectStore("info"); var step2 = function(e){ store.put({key:"id",value:e.target.result.value + 1}).onsuccess = function (e) { …… }; } store.get("id").onsuccess = step2;
前者用到了嵌套回調,后者則需要將業(yè)務流程拆散。
綜上,對indexedDB進行一定的封裝,來簡化編碼操作。
對于這種帶大量回調的API,使用Promise進行異步化封裝是個好主意。
我們可以做如下封裝:
function put(db, table, data ,tx) { return new Promise(function (resolve) { var store = tx.objectStore(table); store.put(data).onsuccess = function (e) { resolve(e); }; }); } var tx = db.transaction("info", "readwrite"); Promise.resolve().then(function(){ put(db, "info", {……}, tx) }).then(function(){ put(db, "info", {……}, tx) });
看上去這么做是沒有問題的,但是實質上,在存儲第二條數(shù)據(jù)時,會報錯并提示事務已被停止。
事務與Promise的沖突When control is returned to the event loop, the implementation MUST set the active flag to false.
——摘自W3C推薦標準(W3C Recommendation 08 January 2015)
如同上面的引用所說,目前的W3C標準要求在控制權回到事件循環(huán)時,當前開啟的事務必須被設置為關閉。因此包括Promise.then在內的所有異步方法都會強制中止當前事務。這就決定了一個事務內部的所有操作必須是同步完成的。
也真是基于這個原因,我沒有在github上找到實現(xiàn)鏈式調用的indexedDB封裝庫。
其中寸志前輩的BarnJS中到是有鏈式調用,然而只是實現(xiàn)了Event.get().then()。也就是只能一次數(shù)據(jù)庫操作,一次結果處理,然后就結束。并不能串聯(lián)多個數(shù)據(jù)庫操作在同一個事務內。
不夠要實現(xiàn)鏈式調用其實也不難,關鍵的問題就在于Promise本身是為異步操作而生的,因此會在鏈式調用的各個函數(shù)中返回事件循環(huán),從而減少網頁的卡頓。所以我們就需要實現(xiàn)一個在執(zhí)行每個函數(shù)過程之間不會返回事件循環(huán)的Promise,也就是一個同步化的Promise。
也許是這個要求太過奇葩,我沒發(fā)現(xiàn)網上有提供同步化執(zhí)行的promise庫。所以只能自己實現(xiàn)一個簡單的。雖然功能不全,但也能湊活用了。下面是使用樣例和詳細代碼解釋,完整代碼見github。
使用樣例// 這句是我封裝過后的用法,等效于: // var tx = new Transaction(db, "info", "readwrite"); var tx = dbObj.transaction("info", "readwrite"); //正常寫法 tx.then(function () { tx.get("info", "a"); tx.get("info", "b"); }).then(function (a, b) { tx.put("info", {key : "c", value : Math.max(a.v, b.v)); }) //偷懶寫法 tx.then(function () { tx.getKV("info", "a"); tx.getKV("info", "b"); }).then(function (a, b) { tx.putKV("info", "c", Math.max(a, b)); })代碼解釋
var Transaction = function (db, table, type) { this.transaction = db.transaction(table, type); this.requests = []; this.nexts = []; this.errorFuns = []; }; Transaction.prototype.then = function (fun) { var _this = this; // 若errored為真則視為已經出錯,直接返回。此時后面的then語句都被放棄。 if (this.errored) { return this; } // 如果當前隊列為空則將自身入隊后,立刻執(zhí)行,否則只入隊,不執(zhí)行。 if (!_this.nexts.length) { _this.nexts.push(fun); fun(_this.results); _this.goNext(); } else { _this.nexts.push(fun); } // 返回this以實現(xiàn)鏈式調用 return _this; };
Transaction的初始化語句和供使用者調用的then語句。
Transaction.prototype.put = function (table, data) { var store = this.transaction.objectStore(table); this.requests.push([store.put(data)]); }; Transaction.prototype.get = function (table, key) { var store = this.transaction.objectStore(table); this.requests.push([store.get(key)]); }; Transaction.prototype.putKV = function (table, k, v) { var store = this.transaction.objectStore(table); this.requests.push([store.put({k, v})]); }; Transaction.prototype.getKV = function (table, key) { var store = this.transaction.objectStore(table); this.requests.push([store.get(key), item=>(item || {}).v]); };
所有的函數(shù)都在發(fā)起數(shù)據(jù)庫操作后將返回的request對象暫存入this.requests中。
目前只實現(xiàn)了put和get,其他的有待下一步工作。另外,getKV和setKV是專門用于存取key-value數(shù)據(jù)的,要求被存取的store包含k,v兩個字段,其中k為主鍵。
// 該語句會在鏈式調用中的每個函數(shù)被執(zhí)行后立刻調用,用于處理結果,并調用隊列中的下一個函數(shù)。 Transaction.prototype.goNext = function () { var _this = this; // 統(tǒng)計前一個函數(shù)塊中執(zhí)行的數(shù)據(jù)庫操作數(shù)量 var total = _this.requests.length; // 清空已完成數(shù)據(jù)庫操作計數(shù)器 _this.counter = 0; // 定義全部操作執(zhí)行完畢或出差后的回調函數(shù) var success = function () { // 當已經有錯誤出現(xiàn)時,放棄下一步執(zhí)行 if (_this.errored) { return; } // 將隊首的節(jié)點刪除,也就是剛剛執(zhí)行完畢的節(jié)點 _this.nexts.shift(); _this.requests = []; // 從返回的event集合中提取出所有result,如果有parser則使用parser。 _this.results = _this.events.map(function (e, index) { if (_this.parser[index]) { return _this.parser[index](e.target.result); } else { return e.target.result; } }); //判斷隊列是否已經執(zhí)行完畢,否則繼續(xù)執(zhí)行下一個節(jié)點 if (_this.nexts.length) { // 將節(jié)點的執(zhí)行結果作為參數(shù)傳給下一個節(jié)點,使用了spread操作符。 _this.nexts[0](..._this.results); _this.goNext(); } }; // 初始化events數(shù)組,清空parser存儲器 _this.events = new Array(total); _this.parser = {}; // 若該請求內不包含數(shù)據(jù)庫操作,則視為已完成,直接調用success if (total === 0) { success(); } // 對于每個請求將請求附帶的parser放入存儲區(qū)。然后綁定onsuccess和onerror。 // 其中onsuccess會在每個請求成功后將計數(shù)器加一,當計數(shù)器等于total時執(zhí)行回調 _this.requests.forEach(function (request, index) { _this.parser[index] = request[1]; request[0].onsuccess = _this.onsuccess(total, index, success); request[0].onerror = _this.onerror; }) };
Transaction.prototype.onsuccess = function (total, index, callback) { var _this = this; return function (e) { // 將返回的event存入event集合中的對應位置 _this.events[index] = e; _this.counter++; if (_this.counter === total) { callback(); } } }; Transaction.prototype.onerror = function (e) { // 設置錯誤標準 this.errored = true; // 保存報錯的event this.errorEvent = e; // 一次調用所有已緩存的catch函數(shù) this.errorFuns.forEach(fun=>fun(e)); }; Transaction.prototype.cache = function (fun) { // 如果已設置錯誤標準則用緩存的event為參數(shù)立刻調用fun,否則將其存入隊列中 if (this.errored) { fun(this.errorEvent); } else { this.errorFuns.push(fun); } };
核心的goNext語句以及success與error的回調。catch類似then用于捕捉異常。
總結好累啊,就這樣吧,以后再加其他功能吧。另外這里面用了不少es6的寫法。所以請務必使用最新版的edge或chrome或firefox運行。或者你可以手動把es6的寫法都去掉。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86187.html
摘要:在不可以用的前提下,無論是同步化或者鏈式操作都用不了。于是昨天我自己實現(xiàn)了一個簡單的同步執(zhí)行的,并以此為基礎實現(xiàn)了鏈式操作。 前言 本來這個系列應該不會這么快更新,然而昨晚寫完前一篇后才發(fā)現(xiàn)我的思路中有一個巨大的漏洞。導致我在前一篇中花費大量時間實現(xiàn)了一個復雜的Transaction類——其實完全有更簡單的方式來完成這一切。前篇:http://segmentfault.com/a/11...
摘要:網上搜來一堆,,幾乎沒有找到滿意的答案,經過匯總并結合自己的理解,封裝了一套簡單的是一個異步對象,必須使用回調函數(shù)方式進行調用打開一個數(shù)據(jù)庫,支持兩個參數(shù),第二個參數(shù)指定版本號,我沒用到,讓瀏覽器自己創(chuàng)建版本號。 網上搜來一堆api,demo,幾乎沒有找到滿意的答案,經過匯總并結合自己的理解,封裝了一套簡單的api // indexedDB是一個異步對象,必須使用回調函數(shù)方式進行調用 ...
摘要:之前我在開發(fā)過程中使用的是,可以直接寫查詢數(shù)據(jù)。,用鍵值模式存儲數(shù)據(jù),而且就是專門為小數(shù)量數(shù)據(jù)設計的。只能是字符串而且空間有限。下面是自己看了阮一峰的文章簡單的學習了下對這個瀏覽器數(shù)據(jù)庫有個大概的了解,下面是個人對簡單的封裝。IndexedDB?瀏覽器數(shù)據(jù)庫,是一個非關系型數(shù)據(jù)庫,數(shù)據(jù)形式使用的是json,IndexedDB適合存儲大量數(shù)據(jù),它的API是異步調用的,當然他的api?也相對復雜...
摘要:離線存儲數(shù)據(jù)的建議對尋址資源,使用這是的一部分。在到達儲量限制之前,兩種存儲機制都會一直進行存儲。則沒有對存儲量做出限制,只是在之后會彈出提醒。是異步的基于回調函數(shù),但它同樣不支持。也是異步的基于回調函數(shù),在和中可以工作雖然使用的是同步。 拖拖拉拉好久,終于把個人博客整出來了。鳴謝 @pinggod。 厚著臉安利一下,地址是 http://www.wemlion.com/。歡迎訪問,歡...
摘要:離線存儲數(shù)據(jù)的建議對尋址資源,使用這是的一部分。在到達儲量限制之前,兩種存儲機制都會一直進行存儲。則沒有對存儲量做出限制,只是在之后會彈出提醒。是異步的基于回調函數(shù),但它同樣不支持。也是異步的基于回調函數(shù),在和中可以工作雖然使用的是同步。 拖拖拉拉好久,終于把個人博客整出來了。鳴謝 @pinggod。 厚著臉安利一下,地址是 http://www.wemlion.com/。歡迎訪問,歡...
閱讀 2796·2021-11-16 11:44
閱讀 969·2021-10-09 09:58
閱讀 4489·2021-09-24 09:48
閱讀 4251·2021-09-23 11:56
閱讀 2407·2021-09-22 15:48
閱讀 1892·2021-09-07 10:07
閱讀 3204·2021-08-31 09:46
閱讀 504·2019-08-30 15:56