Simplify with yield
Targeting modern browsers only? (Building Chrome apps, Firefox apps or Electron Desktop apps or just target modern browsers like Chrome, Opera, Firefox or Edge, Android WebView or Chrome for Mobile, or if you transpile your code from ES6 to ES5)?.
No reason to not start consuming promises using the yield keyword.
The principle is simple:
Use the latest version of Dexie (1.3 or later)
Use Dexie.spawn() or Dexie.async() to enable a synchronous-like programming style.
Each method that returns a Promise can be awaited using yield.
Exactly the same way as ES7's async/await works (but unlike async/await this works in current modern browsers without transpilation).
Sample use
Use in db.transaction()
Dexie.transaction() will treat generator functions (function) so that it is possible to use yield for consuming promises.
async
Marking a generator function as async() will make yield statements behave like ES7 await. This is not needed in transaction callbacks (as above sample shows) but can be used whenever you need to do several transactions, or not use transactions at all.
spawn
Another style is using spawn() instead of async(). Then you don't need to store your async functions in vars.
Calling Sub Functions
There are two possible of structuring your code with sub functions.
Method 1: Declare each function with Dexie.async(). Declaring each function with async is most declarative but requires var declaration with function* expressions instead of function* statements.
Method 2: Just declare as
function* myFunc(){ ... }
. This method gives cleaner code, but it requires the jsdocs to clarify how they are supposed to be consumed. Generator functions are not always used for emulating async/await, so it cannot be assumed that they should be called via spawn() or yield*.
Method 1
NOTE: Using ES5 style vars to make the samples work in today's browsers (March 2016).
Method 2
Rule of thumb is:
Calling a generator function will give you an Iterable, not a Promise.
When awaiting a Promise (for example returned from Dexie API), use
yield
.When awaiting an Iterable (the result from calling a
function*
), useyield*
How this maps to ES7 async / await
Table below shows how this maps to ES7 async / await.
Declare async function
Dexie.async(function* () {});
async function() {}
Declare+execute function
Dexie.spawn(function* () {});
(async function() {})()
Await a Promise
yield p;
await p;
Declare Promise Generator
function* f (){}
N/A
Await Promise Generator
yield* fn();
N/A
Motivation
You can also find the spawn() and async() helpers other libs like Q, Task.js etc. The reason why we need 'yet another' one, is because those will all return their specific types of Promises, which in some browsers are incompatible with indexedDB transactions. That's also the main reason Dexie needs its own Promise implementation. Furthermore, Dexie Promises are capable of maintaining Promise-Specific Data (analogous to Thread-specific data) and utilize that for maintaining transaction scopes and reentrant transaction locks.
However, the Dexie versions of async() and spawn() will adapt to any promise implementation so you can use it to consume other promises as well if you like.