-
-
Notifications
You must be signed in to change notification settings - Fork 664
Simplify with yield
If you 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, there's 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 spawn() or 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 is going to work.
import Dexie from 'dexie';
const async = Dexie.async;
const spawn = Dexie.spawn;
export var db = new Dexie('testdb');
db.version(1).stores({
people: '++id,name,age',
assets: '++id,ownerId,assetName'
});
spawn(function*() {
//
// Write your synchronous-like code within a spawn() block
//
// Add a person:
let id = yield db.people.add({name: 'Foo', age: 34});
// Add two assets that links to the newly added person:
db.assets.bulkAdd([
{assetName: "car", ownerId: id},
{assetName: "house", ownerId: id}
]);
// Now, list all people and their assets:
let people = yield db.people.toArray();
for (let i=0; i<people.length; ++i) {
let assets = yield db.assets
.where('ownerId').equals(people[i].id)
.toArray();
console.log(`${people[i].name}'s assets: ${assets.map(a => a.assetName).join(',')}`);
}
}).then(function() {
//
// spawn() returns a promise that completes when all is done.
//
console.log("Complete");
}).catch(function(e) {
//
// If any error occur in DB or plain exceptions, you
// may catch them here.
//
console.error("Failed: " + e);
});
import Dexie from 'dexie';
const async = Dexie.async;
const spawn = Dexie.spawn;
var db = new Dexie("myDB");
db.version(1).stores({
friends: '++id, name, age'
});
db.open();
db.transaction('rw', db.friends, function*(){
var friendId = yield db.friends.add({name: "Foo", age: 42});
console.log("Got id: " + friendId);
console.log("These are my friends: " + JSON.stringify(yield db.friends.toArray()));
yield db.friends.delete(friendId);
console.log ("Friend successfully deleted.");
}).catch(e => alert ("Oops: " + e));
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.
import Dexie from 'dexie';
const async = Dexie.async;
const spawn = Dexie.spawn;
...
var listFriends = async(function*() {
var friends = yield db.friends.toArray();
return friends;
});
listFriends()
.then(friends => console.log(JSON.stringify(friends)))
.catch(e => console.error (e.stack));
Another style is using spawn() instead of async(). Then you don't need to store your async functions in vars.
import Dexie from 'dexie';
const async = Dexie.async;
const spawn = Dexie.spawn;
...
function* listFriends () {
var friends = yield db.friends.toArray();
return friends;
});
spawn(listFriends)
.then(friends => console.log(JSON.stringify(friends)))
.catch(e => console.error (e.stack));
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-yield versions of async() and spawn() will adapt to any promise implementation by inspecting the first returned Promise instead of always generating a Dexie Promise.
Dexie.js - minimalistic and bullet proof indexedDB library