媒介
原本这个系列应当不会这么快更新,但是昨晚写完前一篇后才发明我的思绪中有一个庞大的破绽。致使我在前一篇中消费大批时候完成了一个庞杂的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数据库来用了,今后我会进一步增加更多的要领。列位亲们,引荐或许珍藏一下呗。