Dexie.js: Browser's incompatibility between native Promise and indexedDB

Background

UPDATE 2019-12-05: Things have changed since this issue was filed:

1. As of the release of Firefox 60 (May 2018), all browsers has become compliant between IndexedDB and their native Promise. 2. Dexie 2.0 has come out with full support for async/await, both native and transpiled, though native async still suffers on older versions of browsers due to this issue.

Site-note: Dexie still works around this limitation in older browsers unless using native (non-transpiled) async functions, which only works well in modern browsers .

/UPDATE

Async await TC39 proposal was finished as of July 2016 and was hard-bound to native Promise. This will be a show-stopper for all non-native promises including Dexie.Promise from entering the async/await sugar world. Transpilers such as Babel and Typescript has until now left an open door for custom promises by allowing the use of a local var Promise = CustomPromise at current module scope. But we simply cannot rely on this anymore.

Problem

So why can’t we just make Dexie use native Promises and get rid of this issue? Here’s the MAJOR reason:

IndexedDB Transactions does not work with native Promises

A recent test made on evergreen browsers at September 21, 2016, verifies the following unpleasant information:

Chrome / OperaIndexedDB plays ball with native Promise
FirefoxIndexedDB <strike>HATES</strike> plays ball with native Promise
EdgeIndexedDB <strike>HATES</strike> plays ball with native Promise
SafariIndexedDB <strike>dislikes native Promise very much</strike> plays almost perfect ball with native Promise

EDIT: Edge has fixed this in latest version Microsoft Edge 38.14393.0.0 / Microsoft EdgeHTML 14.14393 EDIT2: Safari 10.1 has fixed this as well. However, keep away from micro tasks after a transaction has been created (No Promise.resolve().then(…) after transaction creation without using it). EDIT3: Firefox 60 has fixed this also.

Only in Chrome, Opera and latest Edge version, IndexedDB transactions plays well with native Promises. In Safari, it will keep the transaction alive between single Promise.then() calls, but if you wait for several Promise.resolve() after each other, and then continue to try using the transaction, your screwed!

Dexie works around this issue by implementing its own micro-task loop, making its Promise implementation A+ compliant without sacrificing indexedDB compatibility. But now when TC39 has finalized the async / await standard to rely exclusively on native Promise, not only Dexie, but other indexedDB libraries as well, are in a situation where we can’t do async/await with indexedDB and target other than Chromium browsers.

Way Forward

<strike>If all browsers played well with native Promises, we’re actually not that far from switching to generate native Promises in Dexie. We could make it work and still retain transaction state. There is commented-out code in Dexie that would make it possible to use native Promise. The reason it’s been commented out is due to the fact presented in this issue - turning it on would lie to our users, saying they could use native promises with Dexie when the fact is it wont work on other platforms than Chromium.

On the other hand, in case all of Edge, Safari and Firefox would FIX THIS within a year or so, it might be useful after a while, when all browsers out there are updated. But is is worth it? Is it really going to happen so soon? Given the time it has taken for browser vendors to support indexedDB so far (several years), It doesn’t seem likely that all of them should fix in years to come.

So I believe we will change our docs to discourage people from using async await in favour of using yield. That will save it for plain javascript users but be a pain in the XX for typescript users (See #315). They’ll simply need to use the Promise-style within Dexie transactions, or maybe use yield if it could provide any type safety, which I doubt.</strike>

EDIT: Dexie has the support now for native promises and async/await ever since version 2.0. And it makes more sense now as all modern browsers have solved the issue!

Conclusion

<strike>No library on earth will support async/await within IndexedDB transactions and run that code on Firefox or Safari for the coming two years at least (my simple guess). Making Dexie support native Promises will not change that. Due to that fact, we should discourage the use of async / await within transactions for now. </strike>.

EDIT: The above text was written september 2016 but all browsers became compliant in may 2018. Dexie >= 2.0 works with native async functions on all modern browsers (Edge, Chrome, Opera, Safari and Firefox). And ff transpiling the code with Typescript or Babel, it can target also the older versions of browsers including Firefox <60. So I won’t be discouraging async functions anymore.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 2
  • Comments: 35 (3 by maintainers)

Commits related to this issue

Most upvoted comments

The test in the linked gist appears to pass in Safari 10.1 on desktop (and most likely iOS Safari 10.3). That leaves Firefox as the only browser where IDB and Promises don’t play nicely.

Do you have a link to the page(s) that has inaccurate info? Also, please feel free to click the edit button and correct them directly - it will generate a PR for the dexie.js-web repo.

Huh, I totally didn’t notice the edit button! Unfortunately I’m out of office for the next few days so in case I forget to come back to it, a couple of instances I came across today were:

Relevant: Mozilla Intent to implement and ship: spec compliance Promise microtask behavior posted 2 days ago. They are aiming to get the spec compliant behaviour into Firefox 60 (which will also be an Extended Support Release)

@sechel, let’s continue on #374 where I have suggested a way to accomplish what you need to do.

Webcrypto API will break IDB transactions no matter the workarounds done by Dexie to make promises interact. Actually, Dexie transaction zones are maintained also for native promises originating from api:s lite WebCrypto and fetch(). The problem lies in IndexedDB in that it will make the transaction fire off when calling a non-IndexedDB async API.

There is a new proposal going on that would enable IndexedDB transactions to wait for arbritary promises to complete. Until then, we cannot use WebCrypto within transactions.

To store encrypted data, I would suggest either to encrypt it before starting the transaction, or, use a javascript-based (synchronous) crypto API instead of WebCrypto.