Promises + Generators = ♥️
thanks @liip
Promises
Generators
Combined
Promises
Problem
Solution
getDB((db) => {
db.get("Martin", (user) => {
print(user);
}
});
getDB()
.then((db) => db.get("Martin"))
.then(print);
Promise
-
then(fulfillCbk, rejectCbk): Promise
-
catch(rejectCbk)
let getAPromise = function() {
return new Promise((fulfill, reject) => {
fulfill("yay!");
});
};
let getAnAlwaysFulfilledPromise = function() {
return Promise.resolve("yay!");
};
let doMore = () => getAPromise().then((str) =>{
if(str == "yay")
return true;
else
throw "Invalid string generated";
});
Promise Chaining
- then resolves returned Promises
- return or throw in then handlers are treated as accept/reject
- Problem: Scoping
Promise.resolve(1).then((number) => number + 2).then((number) => {
if(number > 3) {
throw "Exceeded 3";
}
else {
return number / 3;
}
}).then((number) => getUser(number)).then((user) => {
if(user.isNice) {
return getReferences(user).then((references) => {
user.references = references;
return user;
});
}
else {
return user;
}
});
Problem
Solution
function(length, callback) {
var i = 0, users = [];
var done = function(user) {
users.push(user);
if(++i == length) {
callback(users);
}
};
for(var j = 0; j < length; ++j) {
getUser(j, done);
}
}
function(length) {
var indexes = [];
for(var j = 0; j < length; ++j) {
indexes.push(j);
}
return all(
indexes.map((i) => getUser(i))
);
}
Promise.all
- Takes an array of promises
- Waits until all promises are resolved
- Returns the results in an array
- In a promise
Generators
Problem
Solution
function() {
let i = 0;
return function(n) {
let ret;
switch(i) {
case 0:
ret = 0;
break;
case 1:
ret = n;
break;
case 2:
ret = n - 2;
break;
}
++i;
return ret;
};
}
function*(n) {
yield 0;
yield n;
return n - 2;
}
function*() {}
- Like a normal function
-
yield, yield*
-
return
- Returns an Iterator when called
Generator
No Generator
function*(j) {
var i = 0;
yield i;
i += j;
i += yield i;
return i;
}
function(j) {
var i = 0;
return i;
}
// yield
function(j, i) {
i += j;
return i;
}
// yield
function(input, j, i) {
i += input;
return i;
}
Iterator Interface
-
next(passInValue): Object
-
{ value: "yielded value", done: false }
-
-
throw(exception): Object
- optional
-
return(passInValue): Object
- always returns done: true
- optional
Iterable Protocol
- Symbol.iterator
- Most base Objects implement it
-
for(var i of iterator)
- Spread operator
let generator = function*(initial) {
let next = yield initial;
yield next;
return "done";
};
let iterable = generator("a");
console.log(iterable.next("b")); // { value: "a", done: false }
console.log(iterable.next()); // { value: "b", done: false }
console.log(iterable.next()); // { value: "done", done: true }
// comprehension and spreading
console.log([...(for(i of generator("e")) i+"asy")]);
// ["easy", "undefinedasy"]
Promises + Generators
Problem
Solution
getDB((db) => {
db.get("Martin", (user) => {
print(user);
}
});
async(function*(arg) {
var db = yield getDB();
var user = yield db.get("Martin");
print(user);
})
Wrapper Function
- Task.jsm in Mozilla Applications
- js version on http://taskjs.org/
- https://www.promisejs.org/generators/
- Some test harnesses have it by default
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
let updateChannels = async(function*(channels) {
let [data, info] = yield Promise.all([
qs.queueRequest(baseURL+"all", headers, requeue),
qs.queueRequest(infoURL+infoArgs, headers, requeue)
]);
if(data.status_code == 200 && info.status_code == 200) {
let followedChannels = data.items.filter((status) => {
return channels.some((channel) => status.name == channel.login);
});
return Promise.all(followedChannels.map((status) => {
return getChannelFromJSON(info.items.find((ch) => ch.id == status.id))
});
}
});
let updateChannels = function(channels, callback) {
qs.queueRequest(baseURL+"all", headers, requeue, function(data) {
qs.queueRequest(infoURL+infoArgs, headers, requeue, function(info) {
if(data.status_code == 200 && info.status_code == 200) {
Promise.all(data.items.filter((status) => {
return channels.some((channel) => status.name == channel.login);
}).map(function(status) {
let channelInfo = info.items.find((ch) => ch.id == status.id);
return getChannelFromJSON(channelInfo))
})).then(callback);
}
});
});
};
You want worse?
Future
ES2016
-
async function
Thank You For Your Attention
Appendix
function(length) {
var indexes = Array.from(function*() {
var i = 0;
while(i < length) {
yield i++;
}
});
all(indexes.map((i) => getUser(i)));
}
Docs
Promises + Generators = ♥️
By Martin Giger
Promises + Generators = ♥️
Asynchronous JavaScript is changing a lot with the upcoming ES2015 (also known as ES6) standard. This talk introduces you to promises and the versatile generators. It also explains Iterables, which are the generic protocol implemented by generators are used throughout ES2015. When generators and promises are combined they result in awesome asynchronous code!
- 3,013