摘要:在不可以用的前提下,無論是同步化或者鏈?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進行封裝。
在不可以用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并進行操作 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ù)庫來用了,以后我會進一步添加更多的方法。各位親們,推薦或者收藏一下唄。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/86179.html
摘要:綜上,對進行一定的封裝,來簡化編碼操作。化的嘗試對于這種帶大量回調(diào)的,使用進行異步化封裝是個好主意。因此包括在內(nèi)的所有異步方法都會強制中止當(dāng)前事務(wù)。這就決定了一個事務(wù)內(nèi)部的所有操作必須是同步完成的。目前只實現(xiàn)了和,其他的有待下一步工作。 前言 本文是介紹我在編寫indexedDB封裝庫中誕生的一個副產(chǎn)品——如何讓indexedDB在支持鏈?zhǔn)秸{(diào)用的同時,保持對事務(wù)的支持。項目地址:htt...
摘要:上一節(jié)講述了的原理及實現(xiàn),這一節(jié)為大家?guī)韮蓚€基于封裝的庫,方便我們在項目實戰(zhàn)中能夠方便采用處理異步。的方法,也是生成一個成功的對象,但是確是將的參數(shù)傳入對象成功回調(diào)中作為成功回調(diào)參數(shù)。對象和的處理方式和調(diào)用方式相同。 上一節(jié)講述了promise的原理及實現(xiàn),這一節(jié)為大家?guī)韮蓚€基于promise封裝的庫,方便我們在項目實戰(zhàn)中能夠方便采用promise處理異步。 一、q庫 www.n...
摘要:而之后,我們得到的是一個是一個對象,我們可以使用語句定義回調(diào)函數(shù),函數(shù)的內(nèi)容呢,則是將讀取到的返回給并繼續(xù)讓從斷點處執(zhí)行。 在上一篇中我們梳理了koa當(dāng)中中間件的洋蔥模型執(zhí)行原理,并實現(xiàn)了一個可以讓洋蔥模型自動跑起來的流程管理函數(shù)。這一篇,我們再來研究一下koa當(dāng)中異步回調(diào)同步化寫法的原理,同樣的,我們也會實現(xiàn)一個管理函數(shù),是的我們能夠通過同步化的寫法來寫異步回調(diào)函數(shù)。 1. 回調(diào)金字...
摘要:設(shè)置為參數(shù)設(shè)置指定索引,并確保唯一性上面主要做了件事打開數(shù)據(jù)庫表新建,并設(shè)置設(shè)置打開數(shù)據(jù)庫表主要就是版本號和名字,沒有太多講的,我們直接從創(chuàng)建開始吧。打開注意事項檢查是否支持版本更新在生成一個實例時,需要手動指定一個版本號。 在知乎和我在平常工作中,常常會看到一個問題: 前端現(xiàn)在還火嗎? 這個我只想說: 隔岸觀火的人永遠(yuǎn)無法明白起火的原因,只有置身風(fēng)暴,才能找到風(fēng)眼之所在 ——『秦時明...
摘要:安裝事件綁定在文件中,當(dāng)安裝成功后,事件就會被觸發(fā)。激活當(dāng)安裝完成后并進入激活狀態(tài),會觸發(fā)事件。這會導(dǎo)致更新得不到響應(yīng)。由兩個構(gòu)成用來顯示系統(tǒng)的通知用來處理下發(fā)的消息這兩個都是建立在在基礎(chǔ)上的,在后臺響應(yīng)推送消息時間,并把他們傳遞給應(yīng)用。 showImg(https://segmentfault.com/img/bVbhbQf?w=1182&h=656); 原文地址: https://...
閱讀 2121·2021-11-23 10:06
閱讀 3482·2021-11-11 16:54
閱讀 3349·2019-08-29 17:31
閱讀 3573·2019-08-29 17:05
閱讀 2173·2019-08-26 13:36
閱讀 2165·2019-08-26 12:17
閱讀 530·2019-08-26 12:12
閱讀 1679·2019-08-26 10:19