Created
December 7, 2011 01:27
-
-
Save c-spencer/1440982 to your computer and use it in GitHub Desktop.
Using yield/generators to flatten async callbacks.
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Yield is Fun</title> | |
</head> | |
<script type="application/javascript;version=1.7"> | |
function __genLoop(generator, callback, onend) { | |
if (callback && typeof callback === 'function') { | |
try { | |
callback(function () { | |
var args = Array.prototype.slice.call(arguments); | |
try { | |
__genLoop(generator, generator.send(args), onend); | |
} catch (err if err instanceof StopIteration) { | |
onend(); | |
} | |
}); | |
} catch (exc) { | |
try { | |
__genLoop(generator, generator.throw(exc), onend); | |
} catch (err if err instanceof StopIteration) { | |
onend(); | |
} | |
} | |
} else { | |
generator.close(); | |
onend(callback); | |
} | |
} | |
function __gen(generator) { | |
return function () { | |
var args = Array.prototype.slice.call(arguments); | |
var callback = args[args.length - 1]; | |
var passed; | |
if (typeof callback != "function") { | |
passed = args; | |
callback = function(){}; | |
} else { | |
passed = args.slice(0, args.length - 1); | |
} | |
var gen = generator.apply({}, passed); | |
__genLoop(gen, gen.next(), callback); | |
} | |
} | |
var fnGen = __gen(function (d) { | |
var a = 10, b, c; | |
console.log("Yielding callback 1, with", d); | |
[a] = yield function (callback) { | |
setTimeout(function () { | |
callback(20); | |
}, 500); | |
}; | |
console.log("Yielding callback 2, with", d); | |
[b, c] = yield function (callback) { | |
setTimeout(function () { | |
callback(1, 15); | |
}, 500); | |
}; | |
console.log("a, b, c, d", a, b, c, d); | |
}); | |
function make_async_work(str) { | |
return function (callback) { | |
setTimeout(function () { | |
callback(str); | |
}, 500); | |
}; | |
} | |
function fail_work(str) { | |
return function (callback) { | |
throw "failure!"; | |
} | |
} | |
function inside_function() { | |
var a, b; | |
try { | |
[a] = yield make_async_work('inside 1'); | |
} catch (exc) { | |
console.log("Caught Exception", exc); | |
a = 'thank'; | |
} | |
try { | |
[b] = yield fail_work('inside 2'); | |
} catch (exc) { | |
console.log("Caught Exception", exc); | |
b = 'goodness'; | |
} | |
console.log("Yielding return", a + b + 'return value'); | |
yield a + b + 'return value'; | |
} | |
function outside_function() { | |
[b] = yield function (callback) { | |
setTimeout(function () { | |
__gen(inside_function)(function (result) { | |
console.log("Result inside b.", result); | |
callback(result); | |
}); | |
}, 500) | |
} | |
console.log("Yielded b"); | |
yield b; | |
} | |
__gen(outside_function)(function (result) { | |
console.log(result + ' outside value'); | |
}); | |
fnGen(17, function (result) { | |
console.log("Finished!", result); | |
}); | |
fnGen(27); | |
</script> | |
<script type="application/coffeescript-fork"> | |
serverRequest = (args..., callback) -> | |
setTimeout -> | |
callback.apply({}, i + 10 for i in args) | |
, 500 | |
# fnGen is an asynchronously called function with an implicit final | |
# completion callback after the defined arguments. | |
# the ~> defines a generatorLoop wrapped function which uses the generator | |
# to sychronise by doing async processing in a yield -> evaluate -> send loop. | |
# <~ defines a blocking call to a asynchronous function, with a completion | |
# callback passed as a final parameter if a function or function call, otherwise | |
# followed by a named completion callback. | |
fnGen = (d) ~> | |
# Implicit completion callback with other arguments | |
[a] <~ serverRequest 20 | |
# Named explicit completion callback | |
[b, c] <~ finish | |
doServerRequest 10, (n) -> | |
finish n, n - 15 | |
# Implicit completion callback with no arguments | |
[e] <~ serverRequest | |
console.log a, b, c, d, e | |
# a = 20 + 10 | |
# b = 10 + 10 | |
# c = 10 + 10 - 15 | |
# d = d | |
# e = undefined | |
# Nesting | |
[f] <~ finish | |
nested = ~> | |
[a] <~ serverRequest 60 | |
[b] <~ serverRequest 40 | |
a + b | |
nested (c) -> | |
finish c | |
# Nesting silly. | |
[f] <~ ~> | |
[a] <~ serverRequest 60 | |
[b] <~ serverRequest 40 | |
a + b | |
console.log f | |
# f = 120 | |
# implicit return yield | |
[a, b, c] | |
fnGen 5, (results) -> | |
console.log(results); # [30, 20, 5] | |
console.log "Finished!" | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment