Skip to content

Instantly share code, notes, and snippets.

@c-spencer
Created December 7, 2011 01:27
Show Gist options
  • Save c-spencer/1440982 to your computer and use it in GitHub Desktop.
Save c-spencer/1440982 to your computer and use it in GitHub Desktop.
Using yield/generators to flatten async callbacks.
<!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