Javascript IndexedDB交易和承诺之间的相互作用不一致

Javascript IndexedDB交易和承诺之间的相互作用不一致,javascript,promise,indexeddb,bluebird,Javascript,Promise,Indexeddb,Bluebird,我在Reddit和上看到了帖子。我们注意到IndexedDB交易和承诺之间存在一些奇怪的不一致 当所有onsuccess事件完成时,IndexedDB事务自动提交。一个复杂的问题是,除了对同一事务执行另一个操作外,您不能在onsuccess回调中执行任何异步操作。例如,您不能在onsuccess中启动AJAX请求,然后在AJAX请求返回一些数据后重用同一事务 承诺与此有什么关系?据我所知,承诺解决方案应该始终是异步的。这意味着您不能在不自动提交IndexedDB事务的情况下使用承诺 (完全披露

我在Reddit和上看到了帖子。我们注意到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
      });
    };