刚开始使用IndexedDb创建了我的第一个项目,并且我在第一次使用时尝试创建一个用于打开和升级数据库的系统。我想使用promises(当前angularJs $q
服务,但我灵活)给我一些保证,可以捕获发生的任何错误,并减少关于失败模式的心理开销推理。我的要求是:
到目前为止我遇到的问题:
onupgraderequired
回调(因此,如果数据库不需要升级,那么在升级完成时得到解决的承诺将永远无法解决,并且调用代码并不知道在连接回调时是否会出现这种情况。)onsuccess
回调被调用 - 因此每次升级都需要顺序链接onsuccess
回调时,versionchange
交易不再有效。我目前的结论是,API基本上对基于承诺的方法持敌对态度。我最好的尝试在下面(简化了一点以便于阅读)。我哪里错了?
var newPromise = function(withDeferred) {
var deferred = $q.defer();
try {
withDeferred(deferred);
} catch (err) {
deferred.reject(err);
}
return deferred.promise;
};
var newTransactionPromise = function(getTransaction) {
return newPromise(function(deferred) {
var transaction = getTransaction();
transaction.oncomplete = function(ev) { deferred.resolve(); };
transaction.onabort = function(ev) { deferred.reject(transaction.error); };
});
};
var migrations = [
function(db) {
return newTransactionPromise(function() {
// throws: The database is not running a version change transaction.
return db
.createObjectStore("entries", { keyPath: 'id', autoIncrement: true })
.transaction;
});
},
function(db) {
return newTransactionPromise(function()
{
var entryStore = db.transaction("entries", "readwrite").objectStore("entries");
entryStore.add({ description: "First task" });
return entryStore.transaction;
});
}
];
var upgradeAndOpen = function() {
return newPromise(function(deferred) {
var latest_version = migrations.length;
var request = indexedDB.open("caesium", latest_version);
request.onupgradeneeded = function(event) {
try {
// create an already resolved promise to start a chain
var setupDeferred = $q.defer();
setupDeferred.resolve();
var setupComplete = setupDeferred.promise;
for (var v = event.oldVersion; v < latest_version; v++)
{
// Problem: the versionchange transaction will be 'inactive' before this promise is scheduled
var nextMigration = migrations[v].bind(this, request.result);
setupComplete = setupComplete.then(nextMigration);
}
setupComplete["catch"](deferred.reject);
} catch (err) {
deferred.reject(err);
}
};
request.onerror = function(event) { deferred.reject(request.error); };
request.onsuccess = function(event) { deferred.resolve(request.result); };
});
};
upgradeAndOpen()["catch"](function(err) { $scope.status = err; });
答案 0 :(得分:1)
var open = function(name, ver) {
return new Promise(function(yes, no) {
var req = indexedDB.open(name, var);
req.onupgradedneeded = function(res) {
no(req);
req.onsuccess = null; // for clarity
};
req.onsuccess = function() {
yes(res.result);
};
req.onblocked = no;
}
});
open('db name', 3).then(function(db) {
// use db here
}, function(req) {
// version upgrade logic here
if (req instanceof IDBResult) {
return new Promise(function(yes, no) {
req.transaction.createObjectStore('store_3');
req.onsuccess = function() {
yes(req.result);
});
});
}
});
答案 1 :(得分:0)
我终于找到了一种方法来避免这个API的所有肮脏,并找到了一个解决方案,它公开了一个干净的基于promises的接口,并推广到任意数量的数据库迁移。关键问题:
versionchange
事务期间无法执行数据更改,因此我们必须区分数据和模式迁移,并使用不同的事务以不同的方式执行它们。versionchange
事务期间执行,但不能通过通常的versionchange
方法执行 - 这会引发异常。而是使用对db.transaction('readwrite', ...).objectstore(...)
交易的引用。versionchange
只允许一个.open(dbName, version)
交易,一旦成功,它就会消失。此方法是创建versionchange
交易versionchange
.open(dbName, version)
事务在其他数据库连接打开时阻塞,因此在尝试链中的下一次迁移之前必须关闭每个连接。我提出的谈判所有这些陷阱的代码如下。
versionchange
答案 2 :(得分:0)
Closure库有promise wrapper for IndexedDB。它的开放数据库方法return a promise和IDB中的所有内容都包含在那里。
所以它与你的想法相似。
以下是示例摘录:
goog.db.openDatabase('mydb', 1, function(ev, db, tx) {
db.createObjectStore('mystore');
}).addCallback(function(db) {
var putTx = db.createTransaction(
[],
goog.db.Transaction.TransactionMode.READ_WRITE);
var store = putTx.objectStore('mystore');
store.put('value', 'key');
goog.listen(putTx, goog.db.Transaction.EventTypes.COMPLETE, function() {
var getTx = db.createTransaction([]);
var request = getTx.objectStore('mystore').get('key');
request.addCallback(function(result) {
...
});
});
我已经从那里开始了,但是结果API没有很好用,所以我发现我已经编写了IndexedDB包装器,再次使用了闭包库。
使用promise包装所有内容的问题不容易使用,您需要跟踪活动事务。如您所见,您将对数据库查询有三个承诺级别:1)db opening promise 2)transaction promise 3)request promise。
你可以通过在整个应用程序中保持db作为参考来消除第一个承诺,但最后两个将会出现。
所以主要是,我将这三个承诺合并到我的图书馆的一个承诺上。