Skip to content

Instantly share code, notes, and snippets.

@rjvdw
Last active June 21, 2016 08:53
Show Gist options
  • Save rjvdw/4f8502e8e7bce7f8209002556ce8d2a7 to your computer and use it in GitHub Desktop.
Save rjvdw/4f8502e8e7bce7f8209002556ce8d2a7 to your computer and use it in GitHub Desktop.
This gist demonstrates how generator functions, iterators and promises work in JavaScript.
/* # Fun With Iterators
*
* This snippet of code demonstrates how generator functions, iterators
* and promises work in JavaScript.
*
* ## Promises
*
* A promise is an object that represents a value that is yet to be
* determined. As soon as this value is known the promise will be
* resolved. If however something went wrong, the promise will be
* rejected.
*
* See [1] for more information.
*
* ## Iterators
*
* An iterator is an object that can be used to produce a sequence of
* values.
*
* See [2] for more information.
*
* ## Generator functions
*
* A generator function is a function which returns a Generator object.
* Generator objects act like iterators. Generator functions are defined
* using the `function*` declaration.
*
* Generator functions may use the keyword `yield` to return a value
* from the Generator. Alternatively the keyword `yield*` may be used to
* yield all values from another iterator.
*
* See [3] and [4] for more information.
*
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
* [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
* [3]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
* [4]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator
*/
'use strict'
const SECONDS_TO_COUNTDOWN = 10
const FLIGHT_DURATION = 30
const CHANCE_TO_EXPLODE_DURING_COUNTDOWN = 0.01
const CHANCE_TO_EXPLODE_DURING_LIFTOFF = 0.1
coroutine(function* simulateRocketLiftoff(secondsToCountDown) {
try {
yield* countdown(secondsToCountDown)
yield liftoff()
console.log('Success!')
}
catch (err) {
console.error(err.message)
}
})(SECONDS_TO_COUNTDOWN)
/**
* Turns a generator function into a coroutine.
*
* The generator may yield promises. Control will be returned to the
* generator as soon as the promise is resolved or rejected.
*
* @link https://en.wikipedia.org/wiki/Coroutine
* @param {generator} gen The generator function to turn into a
* coroutine.
* @return {Function} Starts the coroutine. Arguments passed to this
* function will be passed along to the generator.
*/
function coroutine(gen) {
return function runner() {
const iter = gen.apply(null, arguments)
return loop(iter)
}
/**
* Loops through the iterator that was generated by the generator.
*
* @param {Iterator} iter Iterator generated by the generator.
* @param {*} [prev] The value returned by the last iteration.
* @return {Promise} A promise that resolves when iteration is done.
*/
function loop(iter, prev) {
return handleIteration(iter, iter.next(prev))
}
/**
* Handles a single iteration.
*
* @param {Iterator} iter Iterator generated by the
* Generator.
* @param {IteratorResult} iteration An iteration result.
* @return {Promise} A promise that resolves when iteration is done.
*/
function handleIteration(iter, iteration) {
if (iteration.done) return iteration.value
/*
* If the promise resolves, continue iterating. If it does not,
* throw the error, so that it may be handled from within the
* coroutine.
*/
return iteration.value
.then(value => loop(iter, value))
.catch(err => handleIteration(iter, iter.throw(err)))
}
}
/**
* Simulates the count down before lift off.
*
* There is a chance that the rocket will explode during count down.
*
* @param {Number} from The number of seconds to count down.
* @throws {Error} Thrown if the rocket exploded during count down.
*/
function* countdown(from) {
while (from > 0) {
console.log(`T minus ${from}`)
yield sleep(1)
if (Math.random() < CHANCE_TO_EXPLODE_DURING_COUNTDOWN) {
throw new Error('The rocket exploded during countdown!')
}
from -= 1
}
}
/**
* Simulates the lift off.
*
* There is a chance that the rocket will explode during lift off.
*
* @return {Promise} A promise that will resolve if the lift off was
* successful, or reject if the rocket exploded.
*/
function liftoff() {
console.log('Lift off!')
return sleep(FLIGHT_DURATION)
.then(() => new Promise((resolve, reject) => {
if (Math.random() < CHANCE_TO_EXPLODE_DURING_LIFTOFF) {
reject(new Error('The rocket exploded during liftoff!'))
}
else {
resolve()
}
}))
}
/**
* Sleeps for a given number of seconds.
*
* @param {Number} seconds The number of seconds to sleep.
* @return {Promise} A promise that will resolve after the specified
* number of seconds.
*/
function sleep(seconds) {
return new Promise(resolve => setTimeout(resolve, 1000 * seconds))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment