Created
May 18, 2012 17:24
-
-
Save bjouhier/2726551 to your computer and use it in GitHub Desktop.
Asynchronous hello world with JS generators
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
(function() { | |
var GENERATOR_PROTO = Object.getPrototypeOf((function() { | |
yield; | |
})()); | |
function isGenerator(val) { | |
return typeof val === 'object' && Object.getPrototypeOf(val) === GENERATOR_PROTO; | |
} | |
var PENDING = {}; | |
window.AsyncGen = { | |
run: function(fn, args, idx) { | |
var cb = args[idx], | |
g; | |
function resume(err, val) { | |
while (g) { | |
try { | |
val = err ? g.throw(err) : g.send(val); | |
err = null; | |
// if we get PENDING, the current call completed with a pending I/O | |
// resume will be called again when the I/O completes. So just return here. | |
if (val === PENDING) return; | |
// if we get [PENDING, e, r], the current call invoked its callback synchronously | |
// we just loop to send/throw what the callback gave us. | |
if (val && val[0] === PENDING) { | |
err = val[1]; | |
val = val[2]; | |
} | |
// else, if g yielded a value which is not a generator, g is done. | |
// so we unwind it we send val to the parent generator (or through cb if we are at the top) | |
else if (!isGenerator(val)) { | |
g.close(); | |
g = g.prev; | |
} | |
// else, we got a new generator which means that g called another generator function | |
// the new generator become current and we loop with g.send(undefined) (equiv to g.next()) | |
else { | |
val.prev = g; | |
g = val; | |
val = undefined; | |
} | |
} catch (ex) { | |
// the send/throw call failed. | |
// we unwind the current generator and we rethrow into the parent generator (or through cb if at the top) | |
g.close(); | |
g = g.prev; | |
err = ex; | |
val = undefined; | |
} | |
} | |
// we have exhausted the stack of generators. | |
// return the result or error through the callback. | |
cb(err, val); | |
} | |
// set resume as the new callback | |
args[idx] = resume; | |
// call fn to get the initial generator | |
g = fn.apply(this, args); | |
// start the resume loop | |
resume(); | |
}, | |
invoke: function(that, fn, args, idx) { | |
// Set things up so that call returns: | |
// * PENDING if it completes with a pending I/O (and cb will be called later) | |
// * [PENDING, e, r] if the callback is called synchronously. | |
var result = PENDING, | |
sync = true; | |
var cb = args[idx]; | |
args[idx] = function(e, r) { | |
if (sync) { | |
result = [PENDING, e, r]; | |
} else { | |
cb(e, r); | |
} | |
} | |
fn.apply(that, args); | |
sync = false; | |
return result; | |
} | |
}; | |
})(); |
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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" | |
"http://www.w3.org/TR/html4/loose.dtd"> | |
<html> | |
<head> | |
<script src="asyncgen.js" type="application/javascript;version=1.7"></script> | |
<script src="hello.js" type="application/javascript;version=1.7"></script> | |
</head> | |
<body> | |
<h1>Asynchronous hello world with generators (Firefox only)</h1> | |
<button onClick="javascript:startDemo();return;">Start</button> | |
<div id="output"></div> | |
</body> | |
</html> | |
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
function print(str) { | |
document.getElementById('output').innerHTML += "<p>" + str + "</p>"; | |
} | |
function delay(_, result) { | |
yield AsyncGen.invoke(this, setTimeout, [_, 1000], 0); | |
yield result; | |
} | |
function helloWorld(_) { | |
print("starting ..."); | |
print(yield delay(_, 'hello ...')); | |
print(yield delay(_, '... world')); | |
print("done!"); | |
yield; | |
} | |
function startDemo() { | |
AsyncGen.run(helloWorld, [function(err) { | |
if (err) alert(err); | |
else print("continuing with callback"); | |
}], 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment