Created
September 21, 2011 15:49
-
-
Save nitoyon/1232433 to your computer and use it in GitHub Desktop.
Test Implementation of .NET 5 `await` and `async` in JavaScript 1.7
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
<html> | |
<body onload="init2()"> | |
<script type="application/javascript;version=1.7"> | |
/*===========================================================================* | |
* Test Implementation of .NET 5 `await` and `async` in JavaScript 1.7 | |
*===========================================================================* | |
* !!!!! This sample only works on Firefox !!!!! | |
* (Other browsers don't support `yield` statement) | |
*===========================================================================* | |
* use `async` function like: | |
* // C#: async void func_name(){ } | |
* var func_name = async(function(){ | |
* // function body here | |
* }); | |
* | |
* use `yield` statement instead of `await`: | |
* // C#: ret = await func_name(); | |
* var ret = yield func_name(); | |
* | |
* use `yield` statement in order to return value in `async` function: | |
* // C#: return 3; | |
* yield 3; | |
*===========================================================================*/ | |
/*===========================================================================* | |
* Test Code | |
*===========================================================================* | |
* Output of init1(): | |
* doXhr start | |
* doXhr end | |
* finished: true, status: 200 | |
* finished: false, status: 404 | |
* | |
* Output of init2(): | |
* doXhr start | |
* finished: true, status: 200 | |
* finished: false, status: 404 | |
* doXhr end: 3 | |
*===========================================================================*/ | |
function init1(){ | |
log("doXhr start"); | |
doXhrTwice(); | |
log("doXhr end"); | |
} | |
var init2 = async(function(){ | |
log("doXhr start"); | |
var ret = yield doXhrTwice(); | |
log("doXhr end: " + ret); | |
}); | |
var doXhrTwice = async(function(){ | |
var xhr = awaitable(new XMLHttpRequest()); | |
xhr.open('GET', location.href, true); | |
var result = yield xhr.send(null); | |
log("finished: " + result + ", status: " + xhr.status); | |
xhr = awaitable(new XMLHttpRequest()); | |
xhr.open('GET', "not_found.png", true); | |
result = yield xhr.send(null); | |
log("finished: " + result + ", status: " + xhr.status); | |
yield 3; | |
}); | |
// log output | |
function log(text){ | |
var p = document.createElement("p"); | |
p.appendChild(document.createTextNode(text)); | |
document.getElementById("logContainer").appendChild(p); | |
} | |
/*===========================================================================* | |
* Library starts... | |
*===========================================================================*/ | |
// make object awaitable. | |
// | |
function awaitable(obj){ | |
if (obj.toString() == "[object XMLHttpRequest]"){ | |
var xhr = obj; | |
var _send = xhr.send; | |
xhr.send = function(){ | |
var task = new Task(); | |
xhr.onreadystatechange = function(){ | |
if (xhr.readyState == 4){ | |
if (xhr.status == 200){ | |
task.success(); | |
} else { | |
task.error(); | |
} | |
} | |
}; | |
_send.apply(xhr, arguments); | |
return task | |
}; | |
} | |
return obj; | |
} | |
// Emulate .NET 5 `async` keyword. | |
// Function that contains `await` keyword must be passed to | |
// `async` function. | |
function async(f){ | |
var self = this; | |
return function(){ | |
var task = new Task(); | |
// call original function. | |
var ret = f.apply(self, arguments); | |
// if it doesn't return generator, returns it. | |
if (ret != "[object Generator]"){ | |
return ret; | |
} | |
// call generator's next() method. | |
task.generator = ret; | |
task.goNext(); | |
return task; | |
}; | |
} | |
// Task constructor | |
function Task(){ | |
this.id = ++Task.id; | |
this.generator = null; | |
this.isCompleted = false; | |
this.result = undefined; | |
this.exception = null; | |
if (Task.debug) console.log("task%d: generated", this.id); | |
} | |
Task.id = 0; | |
Task.debug = false; | |
Task.prototype = { | |
goNext: function(){ | |
if (Task.debug) console.log("task%d: goNext called", this.id); | |
// if there's no generator, task is done. | |
if (!this.generator){ | |
this.complete(); | |
return; | |
} | |
// get next child task or result value. | |
try{ | |
var ret; | |
if (this.childTask){ | |
ret = this.generator.send(this.childTask.result); | |
} else { | |
ret = this.generator.next(); | |
} | |
} catch (e) { | |
if (e instanceof StopIteration) { | |
// no child task. | |
if (Task.debug) console.log("task%d: StopIteration", this.id); | |
this.complete(); | |
return; | |
} | |
throw e; | |
} | |
// When yielded object is not task, it is result value. | |
if (ret.__proto__ != Task.prototype){ | |
if (Task.debug) console.log("task%d: return with value %s", this.id, ret); | |
this.result = ret; | |
this.complete(); | |
return; | |
} | |
// task is yielded! call the async function. | |
var childTask = ret; | |
if (Task.debug) console.log("task%d: child task %d is returned", this.id, childTask.id); | |
childTask.parent = this; | |
this.childTask = childTask; | |
}, | |
complete: function(){ | |
if (Task.debug) console.log("task%d: done", this.id); | |
this.isCompleted = true; | |
if (this.generator){ | |
this.generator.close(); | |
} | |
if (this.parent){ | |
this.parent.goNext(); | |
} | |
}, | |
throw: function(ex){ | |
if (Task.debug) console.log("task%d: excepion %s", this.id, ex); | |
this.exception = ex; | |
if (this.parent && this.parent.generator){ | |
this.parent.generator.throw(ex); | |
} | |
}, | |
success: function(){ | |
if (Task.debug) console.log("task%d: success", this.id); | |
this.result = true; | |
this.complete(); | |
}, | |
error: function(){ | |
if (Task.debug) console.log("task%d: error", this.id); | |
this.result = false; | |
this.complete(); | |
} | |
}; | |
</script> | |
<div id="logContainer"> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment