摘要:在不可以用的前提下,無論是同步化或者鏈式操作都用不了。于是昨天我自己實現了一個簡單的同步執行的,并以此為基礎實現了鏈式操作。
前言
本來這個系列應該不會這么快更新,然而昨晚寫完前一篇后才發現我的思路中有一個巨大的漏洞。導致我在前一篇中花費大量時間實現了一個復雜的Transaction類——其實完全有更簡單的方式來完成這一切。
前篇:http://segmentfault.com/a/1190000003982058
項目地址:https://github.com/woodensail/indexedDB
昨天在設計封裝庫時,一開始是打算利用《co模塊的前端實現》中實現的async庫來完成數據庫訪問的同步化。然而嘗試之后就發現并不可行,問題在前篇中提到過,出在Promise的機制上。于是得出結論:indexedDB不可以用原生Promise進行封裝。
在不可以用Promise的前提下,無論是同步化或者鏈式操作都用不了。于是昨天我自己實現了一個簡單的同步執行的Promise,并以此為基礎實現了鏈式操作。
我在昨天的文章寫完之后才突然想到,既然沒有Promise就沒法實現同步化和鏈式操作,那么我自己實現完Promise后完全可以不用使用鏈式操作,直接一步到位實現同步化。所以這篇文章就是關于如何完成indexedDB同步化封裝。
這是一段最基礎的用法,依然和昨天一樣實現了get,put,getKV,putKV,四個函數。
這種寫法下每個操作是多帶帶事務的,無法保證原子性。
async(function* () { var db = yield new DB("test"); yield db.put("info", {k: "async", v: 1}); var result1 = yield db.get("info", "async"); console.log(result1.v); yield db.putKv("info", "async", "1"); var result2 = yield db.getKv("info", "async"); console.log(result2); }).catch(function (e) { console.log(e); });
首先打開一個事務然后作為參數傳遞給數據庫訪問函數即可啟用事務支持。
下面這兩個操作是在同一個事務中的。
async(function* () { var db = yield new DB("test"); var tx = db.transaction("info", "readwrite"); yield db.put("info", {k: "async", v: 1}, tx); var result1 = yield db.get("info", "async", tx); console.log(result1.v); }).catch(function (e) { console.log(e); });
在開啟事務時,第三個參數填true,可以將當前事務作為默認事務。后面的操作將自動使用該事務。需要在使用完畢后調用transactionEnd手動清除默認事務。
async(function* () { var db = yield new DB("test"); db.transaction("info", "readwrite", true); yield db.put("info", {k: "async", v: 1}); var result1 = yield db.get("info", "async"); console.log(result1.v); db.transactionEnd(); }).catch(function (e) { console.log(e); });實現Promise
這是一個不完全版的Promise實現,只提供了最基本的then和catch以及他們的鏈式調用。反正也夠async用了。
暫時沒有提供對Promise.all的支持,我估計要支持得修改async庫才行,所以就以后再說吧。
var DbPromise = function (fun) { this.state = "pending"; this.resolveList = []; this.rejectList = []; var _this = this; fun(function () { _this.resolve.apply(_this, arguments) }, function () { _this.reject.apply(_this, arguments) }); }; DbPromise.prototype = {};
DbPromise.prototype.resolve = function (data) { this.state = "resolved"; this.data = data; var _this = this; this.resolveList.forEach(function (fun) { _this.data = fun(_this.data) }); }; DbPromise.prototype.reject = function (data) { this.state = "rejected"; this.error = data; this.rejectList.forEach(function (fun) { fun(data); }); };
DbPromise.prototype.then = function (fun) { if (this.state === "pending") { this.resolveList.push(fun); } else { this.data = fun(this.data); } return this; }; DbPromise.prototype.catch = function (fun) { if (this.state === "pending") { this.rejectList.push(fun); } else { fun(this.error); } return this; };實現數據庫封裝類
定義class DB,打開數據庫的操作定義在_open中,會在后面介紹
var DB = function (name, upgrade, version) { var _this = this; // 可以在其他js文件中通過向DB.dbMap添加成員的方式來預定義數據庫 // 如果是已經預定義過的數據庫就可以調用new DB(name)來打開 // 否則需要調用new DB(name, upgrade, version),填寫upgrade響應函數和版本號 if (DB.dbMap[name]) { var map = DB.dbMap[name]; return _open(name, map.upgrade, map.version).then(function (db) { _this.db = db; return _this; }).then(map.nextStep); } else { return _open(name, upgrade, version).then(function (db) { _this.db = db; return _this; }); } }; DB.prototype = {};
打開事務和取消關閉事務的方法。
DB.prototype.transaction = function (table, type, asDefault) { // 根據給定的目標和類型打開當前數據庫的事務 var tx = _transaction(this.db, table, type); // 如果asDefault為真,則緩存該事務將其作為默認事務。 // 注意目前同時設置多個默認事務是會沖突的, // 這種情況理論上有極小的可能性在異步操作中出現,我會在接下來的版本中改正這一點。 if (asDefault) { this.tx = tx; } return tx; }; //取消默認事務 DB.prototype.transactionEnd = function () { this.tx = void 0; }; function _transaction(db, table, type) { return db.transaction(table, type); }
具體的數據庫操作函數。其實是對_put等函數的封裝。
// tx || this.tx 指的是優先使用參數指定的事務,其次使用默認事務 DB.prototype.put = function (table, data, tx) { return _put(this.db, table, data, tx || this.tx); }; DB.prototype.get = function (table, name, tx) { return _get(this.db, table, name, tx || this.tx); }; DB.prototype.clear = function (table, tx) { return _clear(this.db, table, tx || this.tx); }; //這兩個是對get和put的特殊封裝,多了參數和結果的預處理,簡化了參數和返回值的格式 DB.prototype.getKv = function (table, k, tx) { return _get(this.db, table, k, tx).then(o=>(o || {}).v); }; DB.prototype.putKv = function (table, k, v, tx) { return _put(this.db, table, {k, v}, tx); };
_open,_put,_get,_clear的實現由于后三者類似,所以只貼了_put。需要后兩點代碼請查看github。
function _open(name, upgrade, version) { // 返回自定義Promise供async庫調用 return new DbPromise(function (resolve, reject) { // 打開指定數據庫的指定版本 var request = indexedDB.open(name, version); // 設置升級操作 request.onupgradeneeded = upgrade; // 綁定success和error,其中成功后會返回打開的數據庫對象 request.onsuccess = function (e) { resolve(request.result); }; request.onerror = reject; }); }
function _put(db, table, data, tx) { // 返回自定義Promise供async庫調用 return new DbPromise(function (resolve, reject) { // 如果沒有提供事務則創建新事務 tx = tx || db.transaction(table, "readwrite"); // 打開store并進行操作 var store = tx.objectStore(table); var request = store.put(data); // 設置回調 request.onsuccess = function () { resolve(request.result); }; request.onerror = reject; }); }總結
基本上,在實現了DbPromise之后其他部分的實現方式就是按老一套來就行了,非常的簡單。我昨天只是光棍節腦袋抽筋沒反應過來而已。
現在的庫已經可以當做基本的Key-Value數據庫來用了,以后我會進一步添加更多的方法。各位親們,推薦或者收藏一下唄。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86179.html
摘要:綜上,對進行一定的封裝,來簡化編碼操作?;膰L試對于這種帶大量回調的,使用進行異步化封裝是個好主意。因此包括在內的所有異步方法都會強制中止當前事務。這就決定了一個事務內部的所有操作必須是同步完成的。目前只實現了和,其他的有待下一步工作。 前言 本文是介紹我在編寫indexedDB封裝庫中誕生的一個副產品——如何讓indexedDB在支持鏈式調用的同時,保持對事務的支持。項目地址:htt...
摘要:上一節講述了的原理及實現,這一節為大家帶來兩個基于封裝的庫,方便我們在項目實戰中能夠方便采用處理異步。的方法,也是生成一個成功的對象,但是確是將的參數傳入對象成功回調中作為成功回調參數。對象和的處理方式和調用方式相同。 上一節講述了promise的原理及實現,這一節為大家帶來兩個基于promise封裝的庫,方便我們在項目實戰中能夠方便采用promise處理異步。 一、q庫 www.n...
摘要:而之后,我們得到的是一個是一個對象,我們可以使用語句定義回調函數,函數的內容呢,則是將讀取到的返回給并繼續讓從斷點處執行。 在上一篇中我們梳理了koa當中中間件的洋蔥模型執行原理,并實現了一個可以讓洋蔥模型自動跑起來的流程管理函數。這一篇,我們再來研究一下koa當中異步回調同步化寫法的原理,同樣的,我們也會實現一個管理函數,是的我們能夠通過同步化的寫法來寫異步回調函數。 1. 回調金字...
摘要:設置為參數設置指定索引,并確保唯一性上面主要做了件事打開數據庫表新建,并設置設置打開數據庫表主要就是版本號和名字,沒有太多講的,我們直接從創建開始吧。打開注意事項檢查是否支持版本更新在生成一個實例時,需要手動指定一個版本號。 在知乎和我在平常工作中,常常會看到一個問題: 前端現在還火嗎? 這個我只想說: 隔岸觀火的人永遠無法明白起火的原因,只有置身風暴,才能找到風眼之所在 ——『秦時明...
摘要:安裝事件綁定在文件中,當安裝成功后,事件就會被觸發。激活當安裝完成后并進入激活狀態,會觸發事件。這會導致更新得不到響應。由兩個構成用來顯示系統的通知用來處理下發的消息這兩個都是建立在在基礎上的,在后臺響應推送消息時間,并把他們傳遞給應用。 showImg(https://segmentfault.com/img/bVbhbQf?w=1182&h=656); 原文地址: https://...
閱讀 2111·2021-11-23 10:06
閱讀 3476·2021-11-11 16:54
閱讀 3343·2019-08-29 17:31
閱讀 3569·2019-08-29 17:05
閱讀 2171·2019-08-26 13:36
閱讀 2159·2019-08-26 12:17
閱讀 524·2019-08-26 12:12
閱讀 1673·2019-08-26 10:19