Generators
Generators are functions that can be paused in mid execution. You recognize a generator by declaring itself with a *
in the function prototype and a yield
in the function body :
function * generator(){
yield 1;
yield 2;
}
This function can be called multiple times with different results. It will move its pointer, if you will from one yield
to the next. To get the value from the generator function you need to call the next()
method. This will return an object consisting of the properties value
and done
.
Like so:
let gen = generator();
gen.next() // { value: 1, done: false }
gen.next() // { value: 2, done: false }
gen.next() // { value: undefined, done: true }
Sending values to your generator
function *generator(){
var x = yield 1;
yield "hi" + x;
}
Calling the generator like this:
var gen = generator();
gen.next(); // { value: 1, done : false }
gen.next('chris') // { value: 'hi chris', done : false }
What happens here is that the first yield
is being triggered when calling next()
. On the second call to next()
the statement yield 1
is being replaced with what we send into the next()
statement which is chris
so it reads like hi chris
, which is what we are getting out.
Using it with promises
Using generators with promises is quite easy. What you need is to make sure that what you are yielding is instead a promise like so:
function getData(){
return Promise.resolve(5);
}
function* generator(){
yield getData();
}
var gen = generator();
gen.next().value
.then(function(data){
console.log(data) // 5
})
Using it with co lib
Co is a fanastic little lib found at link to co It can be easily installed with:
npm install co
It works like the following:
function getData(){
return Promise.resolve( 1 )
}
function getMoreData(){
return Promise.resolve( 2 );
}
function evenMore(){
return Promise.resolve( 3 );
}
function getError(){
return Promise.reject( 'err' );
}
co(function* (){
try {
var data = getData();
var moreData = getMoreData();
var evenMore = evenMore();
} catch (err) {
console.log(err) // err
}
})
To see why this is great for existing apps let's look at how it can really transform an AngularJS project.
not using co
angular.module('app', [])
.controller('ctrl', function(){
let vm = this;
function init(){
authorize()
.then(getUser)
.then(getOrdersByUser)
.catch((err) => vm.error = err)
}
init();
})
using co
angular.module('app', [])
.controller('ctrl', function(){
let vm = this;
co(function* (){
try {
var authorized = authorize();
var user = getUser( authorized );
var evenMore = getOrdersByUser( user.id );
vm.data = { data : data, moreData : moreData, evenMore : evenMore }
} catch (err) {
vm.error = err;
}
})
})
It should also be mentioned that co
returns a Promise so you are able to do the following:
co(function*(){
var data = yield getData();
return data
})
.then( data => console.log(data) )
.catch( err => console.error(err) )
The number of lines of code is about the same but it is so much more readable without then()
and catch()
and it looks synchronous - achievement unlocked
Here is a github repo showing how you could use it with AngularJS Angular with generators