Created
January 29, 2017 05:24
-
-
Save dezull/d8ad789f5025e53eee185f0f34e0ca0b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
let http = require('http'); | |
// Generator function, wrapper for http.get. | |
function *get(url) { | |
// Each generator function should call yield initially, | |
// to get the caller object, which contains done(result) | |
// and fail(error) functions. Pass the argument when | |
// done/failed doing asynchronous stuff appropriately. | |
const caller = yield; | |
http.get(url, (res) => { | |
let rawData = ''; | |
res.on('data', (chunk) => rawData += chunk); | |
res.on('end', () => { | |
try { | |
caller.done(JSON.parse(rawData)); | |
} catch (e) { | |
caller.fail(e); | |
} | |
}); | |
}); | |
} | |
// Generator function, just setTimeout | |
function *timeout(ms) { | |
// See *get for the first yield statement | |
const caller = yield; | |
setTimeout(() => { | |
caller.done('after ' + ms); | |
}, ms); | |
} | |
// This is our generator functions runner. | |
// | |
// Pass a generator function here, so you could use | |
// yield statements to pass a bunch of generator functions, | |
// and they will run one after another. | |
function run(genFn) { | |
let gen = genFn(); | |
// Where we store the current generator object yielded in the runner block | |
let yielded; | |
// The caller object, each generator should call either done or fail | |
// once done with asynchronous work. | |
let result = { | |
done: (data) => { | |
// console.log('done', data); | |
yielded = gen.next(data); | |
resume(); | |
}, | |
fail: (error) => { | |
// console.error('error', error); | |
yielded = gen.throw(error); | |
resume(); | |
}, | |
}; | |
// Resume to the next generator object in the runner block | |
function resume() { | |
if (yielded.done) return; | |
// yielded.value is the current generator object | |
let genFromYield = yielded.value; | |
// This will start the generator | |
let r = genFromYield.next(); | |
if (!r.done) { | |
// This will stop at the `const caller = yield;` of the generator | |
r = genFromYield.next(result); | |
} | |
} | |
// Now, start with the first generator | |
yielded = gen.next(); | |
resume(); | |
} | |
console.log('start'); | |
// So here is our runner for generators. | |
// We just need to 'yield' generator functions (not object) here, | |
// Each will run one after another, like synchronously | |
let runnerDone = false; | |
run(function* () { | |
console.log('runner start'); | |
let ms1 = yield timeout(600); | |
console.log('>> timeout', ms1); | |
// try passing an invalid URL here, see how it will stop the runner | |
// when an error is thrown | |
try { | |
let json = yield get('http://nodejs.org/dist/index.json'); | |
console.log('>> get %s ...', JSON.stringify(json).replace(/\s*/g, '').substr(0, 100)); | |
} catch (e) { | |
console.error('>> get', e); | |
} | |
let ms2 = yield timeout(500); | |
console.log('>> timeout', ms2); | |
console.log('runner end'); | |
runnerDone = true; | |
}); | |
// Just for showing, outside the runner, the event loop still runs normally | |
let timer = setInterval(() => { | |
console.log('look ma, no blocking!'); | |
if (runnerDone) clearInterval(timer); | |
}, 200); | |
console.log('end'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment