Javascript IndexedDB交易和承诺之间的相互作用不一致
我在Reddit和上看到了帖子。我们注意到IndexedDB交易和承诺之间存在一些奇怪的不一致 当所有Javascript IndexedDB交易和承诺之间的相互作用不一致,javascript,promise,indexeddb,bluebird,Javascript,Promise,Indexeddb,Bluebird,我在Reddit和上看到了帖子。我们注意到IndexedDB交易和承诺之间存在一些奇怪的不一致 当所有onsuccess事件完成时,IndexedDB事务自动提交。一个复杂的问题是,除了对同一事务执行另一个操作外,您不能在onsuccess回调中执行任何异步操作。例如,您不能在onsuccess中启动AJAX请求,然后在AJAX请求返回一些数据后重用同一事务 承诺与此有什么关系?据我所知,承诺解决方案应该始终是异步的。这意味着您不能在不自动提交IndexedDB事务的情况下使用承诺 (完全披露
onsuccess
事件完成时,IndexedDB事务自动提交。一个复杂的问题是,除了对同一事务执行另一个操作外,您不能在onsuccess
回调中执行任何异步操作。例如,您不能在onsuccess
中启动AJAX请求,然后在AJAX请求返回一些数据后重用同一事务
承诺与此有什么关系?据我所知,承诺解决方案应该始终是异步的。这意味着您不能在不自动提交IndexedDB事务的情况下使用承诺
(完全披露:演示是由paldepind创建的,不是我!)
我已经在Chrome和Firefox上试过了。由于事务自动提交,它在Firefox中失败,但实际上它在Chrome中工作!哪种行为是正确的?如果Firefox的行为是正确的,那么在IndexedDB事务中使用“正确的”promise实现真的是不可能的吗
另一个复杂问题是:如果我在运行上面的演示之前加载,它在Chrome和Firefox中都能工作。这是否意味着蓝鸟正在同步解决承诺?我以为它不应该那样做
这可能是由于。Firefox从未有过使用微任务的标准实现,而Chrome、Bluebird和其他浏览器则正确地使用了微任务。你可以从微任务(比宏任务“更快”执行,但仍然是异步的)如何落在事务边界内看到这一点,而宏任务(例如,来自Firefox的承诺)则不在事务边界内
这是一个Firefox bug。好的,我再次深入研究了IndexedDB、DOM和HTML规范。我真的需要把它做好,因为它在很大程度上依赖于内部交易的承诺 问题的关键在于,
onCompleted
和onRejected
回调的延迟执行是否会触发IndexedDB事务提交,Promissions/A+compliant必须显示该回调
从规范中提取并排列事务生命周期的IndexedDB规则实际上非常简单:
- 只有当事务的活动标志设置为true()时,才能针对事务发出请求
- 创建事务时,它最初处于活动状态,直到控件返回到浏览器事件循环()
- 每次触发成功或错误事件时,事务活动标志将设置为true before,作为调度事件之前的最后一步。事件分派后,事务再次标记为非活动(
- 当事务不再处于活动状态时,它将自动提交()
- 当您创建一个事务时,您可以根据需要对其提出任意多的请求
- 从那时起,新请求只能在事件处理程序中为另一个请求发出
或成功
事件侦听器错误
- 当所有请求都已执行且未放置新请求时,事务将提交
请求的成功或错误事件侦听器中实现了承诺,那么在IndexedDB将事务再次设置为非活动状态之前,是否会调用其oncompleted
回调?即onFullfilled
回调是否作为中步骤3的一部分
该步骤分派一个事件,IndexedDB使用DOM事件,因此实际执行的操作超出了IndexedDB规范。相反,分派事件的步骤是,。通过这些步骤,可以清楚地看出,在任何时候都不是微任务(这将调用promise回调)检查点已执行。因此,最初的结论是,在调用任何oncompleted
回调之前,事务将被关闭
但是,如果我们通过在请求
对象上指定onsuccess
属性来附加事件侦听器,事情会变得更加棘手。在这种情况下,我们不仅仅是按照DOM规范进行的,而是按照HTML规范中的定义设置
当我们这样做时,回调不会直接添加到事件侦听器列表中。而是在中“包装”回调。此算法执行以下重要操作:
在步骤3中,它运行
然后执行以下步骤:
最后,这将执行一个。这意味着您的承诺回调将在事务被标记为非活动之前被调用
这是个好消息!但奇怪的是,答案如何取决于您是否使用addEventListener
侦听success
事件或设置onsuccess
事件处理程序来侦听success
事件。如果您执行前者,则在调用承诺的OnCompleted
回调时,事务应处于非活动状态,如果执行e之后,它应该仍然处于活动状态
然而,我无法重现现有浏览器中的差异。使用本机承诺,Firefox无论在什么情况下都会在示例代码中失败,Chrome甚至在使用addEventListener
时也会成功。我可能忽略或误解了规范中的某些内容
最后请注意,Bluebird promises将在Internet Explorer 11中关闭事务。这是由于Bluebird在IE中使用的调度。我在IE中的事务中工作。您是正确的:承诺是异步解析的,IndexedDB有一些同步要求。而其他答案指出,本机承诺可能有效正确地说,在某些浏览器的某些版本中使用IndexedDB,很可能
var openRequest = indexedDB.open("library");
openRequest.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = openRequest.result;
var store = db.createObjectStore("books", {keyPath: "isbn"});
var titleIndex = store.createIndex("by_title", "title", {unique: true});
var authorIndex = store.createIndex("by_author", "author");
// Populate with initial data.
store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
};
function getByTitle(tx, title) {
return new Promise(function(resolve, reject) {
var store = tx.objectStore("books");
var index = store.index("by_title");
var request = index.get("Bedrock Nights");
request.onsuccess = function() {
var matching = request.result;
if (matching !== undefined) {
// A match was found.
resolve(matching);
} else {
// No match was found.
console.log('no match found');
}
};
});
}
openRequest.onsuccess = function() {
var db = openRequest.result;
var tx = db.transaction("books", "readonly");
getByTitle(tx, "Bedrock Nights").then(function(book) {
console.log('First book', book.isbn, book.title, book.author);
return getByTitle(tx, "Quarry Memories");
}).then(function(book) {
console.log('Second book', book.isbn, book.title, book.author);
// With native promises this gives the error:
// InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
// With bluebird everything is fine
});
};