On Github hoeck / dresdenjs-generators-part-two
function* range (start, end) {
while (start < end) {
yield start++;
}
}
const gen = range(5, 7);
gen.next() // => {value: 5, done: false}
gen.next() // => {value: 6, done: false}
gen.next() // => {done: true}
function* defines a generator function yield produces the generators values calling it creates a generator instance next() delivers the generators values next() delivers the generators values until there are no more values
Example: web application handler, synchronous version
function handler (sessionid, response):
const session = loadSessionSync(sessionid);
const user = loadUserSync(session.userId);
response.send(`Hello ${user.name}`);
load a session from the db and wait load a user from the db and wait create the response
function handler (sessionid, response) {
loadSession(sessionid, function (err, session) {
if (err) response.status(500).send();
loadUser(session.userId, function (err, user) {
if (err) response.status(500).send();
response.send(`hello ${user.name}`);
})
});
};
load a session from the db, when done, invoke a callback load a user from the db, when done, invoke a callback create and send the response deal with errors in each callback
loadUser(userId, function (err, user) {
response.send(`hello ${user.name}`);
}
function handler (sessionid, response) {
loadSession(sessionid).then(function (session) {
return loadUser(session.userId);
}).then(function (user) {
response.send(`hello ${user.name}`);
}).catch(function (err) {
response.status(500).send();
});
};
load a session from the db, when done, return a promise load a user from the db, when done, return a promoise create and send the response error handling for the promise chain
loadSession(sessionid).then(function (session) {
return loadUser(session.userId);
})
function * handler (sessionid, response) {
const session = yield loadSession(sessionId);
const user = yield loadUser(session.userId);
response.send(`hello ${user.name}`);
}
load a session from the db, when done, keep it in session load a user from the db, when done, keep it in user create and send the response errors are thrown and can be catched (e.g. by the framework)
// blocking
function handler (sessionid, response) {
const session = loadSessionSync(sessionid);
const user = loadUserSync(session.userId);
response.send(`Hello ${user.name}`);
}
// non-blocking
function * handler (sessionid, response) {
const session = yield loadSession(sessionId);
const user = yield loadUser(session.userId);
response.send(`hello ${user.name}`);
}
the same, except yield the same, except yield
with generators
to actually write non-blocking generator code
downloading files
import co from co;
import fs from 'mz/fs';
import request from 'request-promise';
const loadFiles = co.wrap(function* (targets) {
for (target of targets) {
const data = yield request.get(target.url);
yield fs.writeFile(target.name, data);
}
});
loadFiles(files).then(() => {
console.log('done');
}).catch((err) => {
console.log(err.stack);
});
co: transforms a generator into a promise mz/fs: nodejs filesystem module using promises request-promise: http client using promises
the generator co.wrap turns a generator into a Promise loop through all targets request.get(target.url) fetches an url and returns a Promise yield takes the promise and passes execution to co const data: once the promise is resolved, co consumes the generator fs.writeFile writes data to a file, when done, the loop continues
calling loadFiles returns a promise
ES7 async & await
function * handler (sessionid, response) {
const session = yield loadSession(sessionId);
const user = yield loadUser(session.userId);
response.send(`hello ${user.name}`);
}
const promise = co.wrap(handler)(sessionid, response);
// turns into
async function handler () {
const session = await loadSession(sessionId);
const user = await loadUser(session.userId);
response.send(`hello ${user.name}`);
}
const promise = handler(sessionid, response);
function * async function yield await and you don't need co