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