Inconsistent interplay between IndexedDB transactions and Promises

后端 未结 3 1956
旧时难觅i
旧时难觅i 2020-12-31 09:54

I saw sync-promise posted on Reddit and got into a discussion with the author. We noticed some weird inconsistencies in the relationship between IndexedDB transactions and p

3条回答
  •  孤城傲影
    2020-12-31 10:56

    Ok, so I've once again taken a deep dive into the IndexedDB, the DOM and the HTML specification. I really need to get this right for SyncedDB since it relies heavily on promises inside transactions.

    The crux of the problem is whether or not the delayed execution of the onFulfilled and the onRejected callbacks to then that Promises/A+ compliant must exhibit will trigger an IndexedDB transaction commit.

    The IndexedDB rules for a transactions lifetime are actually pretty straight forward when you extract them from the specification and line them up:

    • Request can only made against a transaction when its active flag is set to true (as specified here).
    • When a transaction is created it is initially active until control is returned to the the browsers event loop (this is specified in the transaction creation steps).
    • Every time a success or error event is fired, the transactions active flag is set to true before as the last step before the event is dispatched. After the event dispatch the transaction is flagged as inactive again (this is specified in the steps for firing a success/error event.
    • When a transaction can no longer become active it will automatically be committed (as specified here).

    This roughly translates to:

    • When you create a transaction you can place as many request against it as you wish.
    • From then on new request can only be made inside event handlers for another requests success or error event listener.
    • When all requests has been executed and no new requests placed the transaction will commit.

    The question then becomes: If a promise is fulfilled inside a request's success or error event listener will its onFulfilled callbacks be invoked before the IndexedDB sets the transaction as inactive again? I.e. will onFullfilled callbacks be called as part of step 3 in firing a success event?

    The step dispatches an event and IndexedDB uses DOM events so the actual operation performed is beyond the IndexedDB specification. The steps for dispatching an event is, instead, specified here in the DOM specification. Going over the steps it becomes clear that at no point is a microtask (which would call the promise callbacks) checkpoint performed. So the initial conclusion is that the transaction will be closed before any onFulfilled callbacks will be invoked.

    However, if we attach the event listeners by specifying an onsuccess attribute on the request object things gets more hairy. In that case we are not simply adding an event listener as per the DOM specification. We are instead setting an event handler IDL attribute as defined in the HTML specification.

    When we do that the callback is not added directly to the list of event listeners. It is instead "wrapped" inside the the event handlers processing algorithm. This algorithm performs the following important operations:

    1. In step 3 it runs the jump to code entry-point algorithm.
    2. This then performs the steps to clean up after running a callback which
    3. Finally, this performs a microtask checkpoint. Which means that your promise callbacks will be invoked before the transaction is marked as inactive! Hurrah!

    This is good news! But it is weird how the answer depends on whether or not you listen for the success event by using addEventListener or set a onsuccess event handler. If you do the former the transaction should be inactive when your promise's onFulfilled callbacks is invoked and if you do the later it should still be active.

    I was, however not able to reproduce the difference in existing browsers. With native promises Firefox fails at the example code no matter what and Chrome succeeds even when using addEventListener. It is possible that I've overlooked or misunderstood something in the specifications.

    As a final note Bluebird promises will close transactions in Internet Explorer 11. This is due to the scheduling that Bluebird uses in IE. My synchronized promise implementation works inside transactions in IE.

提交回复
热议问题