Last active
August 29, 2015 14:22
-
-
Save cqfd/32f875e8d4705a5230fb to your computer and use it in GitHub Desktop.
Coroutines and promises in Node.
This file contains hidden or 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
// Run with `node --harmony coro.js` | |
"use strict"; | |
const Promise = require('bluebird'); | |
// Takes a function*, `g`, that yields promises. | |
// Returns a promise that resolves to `g`'s eventual return value. | |
var coro = function(gStar) { | |
const pending = Promise.pending(); | |
const gArgs = Array.prototype.slice.call(arguments, 1); | |
const gen = gStar.apply(undefined, gArgs); | |
let result; | |
try { result = gen.next(); } | |
catch (e) { | |
pending.reject(e); | |
return pending.promise; | |
} | |
if (result.done) { | |
pending.resolve(result.value); | |
} else { | |
trampoline(gen, result.value, pending); | |
} | |
return pending.promise; | |
}; | |
var trampoline = function(gen, promise, pending) { | |
promise.then(function(input) { | |
let result; | |
try { result = gen.next(input); } | |
catch (e) { | |
pending.reject(e); | |
return; | |
} | |
if (result.done) { | |
pending.resolve(result.value); | |
} else { | |
trampoline(gen, result.value, pending); | |
} | |
}).catch(function(e) { | |
let result; | |
try { result = gen.throw(e); } | |
catch (e) { | |
pending.reject(e); | |
return; | |
} | |
if (result.done) { | |
pending.resolve(result.value); | |
} else { | |
trampoline(gen, result.value, pending); | |
} | |
}); | |
}; | |
var sleep = function*(ms) { | |
return yield Promise.delay(ms); | |
}; | |
var apiCall = function*(endpoint) { | |
yield* sleep(2000); | |
return { users: ["Foo", "Bar", "Baz"] }; | |
}; | |
var buggyApi = function*(endpoint) { | |
yield Promise.reject(endpoint + " failed!"); | |
}; | |
var bang = function*() { | |
throw "bang!"; | |
}; | |
var outer = function*() { | |
yield* inner(); | |
}; | |
var inner = function*() { | |
yield* sleep(1000); | |
yield* bang(); | |
}; | |
var example = function*(ms) { | |
console.log("Let's sleep for", ms, "milliseconds."); | |
yield* sleep(ms); | |
console.log("And let's now make an expensive api call."); | |
const result = yield* apiCall("/users"); | |
try { yield* bang(); } | |
catch (e) { console.log("bang error", e); } | |
try { yield* outer(); } | |
catch (e) { console.log("outer error", e); } | |
try { yield* buggyApi("/badroute"); } | |
catch (e) { console.log("buggy api", e) } | |
console.log("And let's finish by running some logic."); | |
return result.users.map(user => user.toLowerCase()); | |
}; | |
coro(example, 500) | |
.then(result => console.log("result =", result)) | |
.catch(e => console.log("We got an error!", e)); | |
coro(function*() { | |
let count = 0; | |
while (true) { | |
yield* sleep(100); | |
console.log("tick"); | |
count += 1; | |
if (count > 50) { throw "time's up!" } | |
} | |
}).catch(e => console.log(e)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment