摘要:在不可以用的前提下,無論是同步化或者鏈?zhǔn)讲僮鞫加貌涣?。于是昨天我自己實現(xiàn)了一個簡單的同步執(zhí)行的,并以此為基礎(chǔ)實現(xiàn)了鏈?zhǔn)讲僮鳌?/p>
前言
本來這個系列應(yīng)該不會這么快更新,然而昨晚寫完前一篇后才發(fā)現(xiàn)我的思路中有一個巨大的漏洞。導(dǎo)致我在前一篇中花費大量時間實現(xiàn)了一個復(fù)雜的Transaction類——其實完全有更簡單的方式來完成這一切。
前篇:http://segmentfault.com/a/1190000003982058
項目地址:https://github.com/woodensail/indexedDB
昨天在設(shè)計封裝庫時,一開始是打算利用《co模塊的前端實現(xiàn)》中實現(xiàn)的async庫來完成數(shù)據(jù)庫訪問的同步化。然而嘗試之后就發(fā)現(xiàn)并不可行,問題在前篇中提到過,出在Promise的機制上。于是得出結(jié)論:indexedDB不可以用原生Promise進(jìn)行封裝。
在不可以用Promise的前提下,無論是同步化或者鏈?zhǔn)讲僮鞫加貌涣?。于是昨天我自己實現(xiàn)了一個簡單的同步執(zhí)行的Promise,并以此為基礎(chǔ)實現(xiàn)了鏈?zhǔn)讲僮鳌?br>我在昨天的文章寫完之后才突然想到,既然沒有Promise就沒法實現(xiàn)同步化和鏈?zhǔn)讲僮?/strong>,那么我自己實現(xiàn)完P(guān)romise后完全可以不用使用鏈?zhǔn)讲僮?,直接一步到位實現(xiàn)同步化。所以這篇文章就是關(guān)于如何完成indexedDB同步化封裝。
這是一段最基礎(chǔ)的用法,依然和昨天一樣實現(xiàn)了get,put,getKV,putKV,四個函數(shù)。
這種寫法下每個操作是多帶帶事務(wù)的,無法保證原子性。
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); });
首先打開一個事務(wù)然后作為參數(shù)傳遞給數(shù)據(jù)庫訪問函數(shù)即可啟用事務(wù)支持。
下面這兩個操作是在同一個事務(wù)中的。
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); });
在開啟事務(wù)時,第三個參數(shù)填true,可以將當(dāng)前事務(wù)作為默認(rèn)事務(wù)。后面的操作將自動使用該事務(wù)。需要在使用完畢后調(diào)用transactionEnd手動清除默認(rèn)事務(wù)。
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); });實現(xiàn)Promise
這是一個不完全版的Promise實現(xiàn),只提供了最基本的then和catch以及他們的鏈?zhǔn)秸{(diào)用。反正也夠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; };實現(xiàn)數(shù)據(jù)庫封裝類
定義class DB,打開數(shù)據(jù)庫的操作定義在_open中,會在后面介紹
var DB = function (name, upgrade, version) { var _this = this; // 可以在其他js文件中通過向DB.dbMap添加成員的方式來預(yù)定義數(shù)據(jù)庫 // 如果是已經(jīng)預(yù)定義過的數(shù)據(jù)庫就可以調(diào)用new DB(name)來打開 // 否則需要調(diào)用new DB(name, upgrade, version),填寫upgrade響應(yīng)函數(shù)和版本號 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 = {};
打開事務(wù)和取消關(guān)閉事務(wù)的方法。
DB.prototype.transaction = function (table, type, asDefault) { // 根據(jù)給定的目標(biāo)和類型打開當(dāng)前數(shù)據(jù)庫的事務(wù) var tx = _transaction(this.db, table, type); // 如果asDefault為真,則緩存該事務(wù)將其作為默認(rèn)事務(wù)。 // 注意目前同時設(shè)置多個默認(rèn)事務(wù)是會沖突的, // 這種情況理論上有極小的可能性在異步操作中出現(xiàn),我會在接下來的版本中改正這一點。 if (asDefault) { this.tx = tx; } return tx; }; //取消默認(rèn)事務(wù) DB.prototype.transactionEnd = function () { this.tx = void 0; }; function _transaction(db, table, type) { return db.transaction(table, type); }
具體的數(shù)據(jù)庫操作函數(shù)。其實是對_put等函數(shù)的封裝。
// tx || this.tx 指的是優(yōu)先使用參數(shù)指定的事務(wù),其次使用默認(rèn)事務(wù) 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的特殊封裝,多了參數(shù)和結(jié)果的預(yù)處理,簡化了參數(shù)和返回值的格式 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的實現(xiàn)由于后三者類似,所以只貼了_put。需要后兩點代碼請查看github。
function _open(name, upgrade, version) { // 返回自定義Promise供async庫調(diào)用 return new DbPromise(function (resolve, reject) { // 打開指定數(shù)據(jù)庫的指定版本 var request = indexedDB.open(name, version); // 設(shè)置升級操作 request.onupgradeneeded = upgrade; // 綁定success和error,其中成功后會返回打開的數(shù)據(jù)庫對象 request.onsuccess = function (e) { resolve(request.result); }; request.onerror = reject; }); }
function _put(db, table, data, tx) { // 返回自定義Promise供async庫調(diào)用 return new DbPromise(function (resolve, reject) { // 如果沒有提供事務(wù)則創(chuàng)建新事務(wù) tx = tx || db.transaction(table, "readwrite"); // 打開store并進(jìn)行操作 var store = tx.objectStore(table); var request = store.put(data); // 設(shè)置回調(diào) request.onsuccess = function () { resolve(request.result); }; request.onerror = reject; }); }總結(jié)
基本上,在實現(xiàn)了DbPromise之后其他部分的實現(xiàn)方式就是按老一套來就行了,非常的簡單。我昨天只是光棍節(jié)腦袋抽筋沒反應(yīng)過來而已。
現(xiàn)在的庫已經(jīng)可以當(dāng)做基本的Key-Value數(shù)據(jù)庫來用了,以后我會進(jìn)一步添加更多的方法。各位親們,推薦或者收藏一下唄。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/86179.html