Skip to content

Instantly share code, notes, and snippets.

@nitoyon
Created September 21, 2011 15:49
Show Gist options
  • Save nitoyon/1232433 to your computer and use it in GitHub Desktop.
Save nitoyon/1232433 to your computer and use it in GitHub Desktop.
Test Implementation of .NET 5 `await` and `async` in JavaScript 1.7
<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