
This might not be a text for wizards of javascript. But I think, it might be very interesting for the other, much larger part of DOM/javascript developers population?
And (I dare to suggest) some from the narrow circle of self named javascript “ninjas” may learn how to apply object oriented programming paradigms and design patterns to contemporary javascript functional programming idioms.
For Asynchronous JavaScript 2/3 please click here.
Jan 23, 2009
The other day I wanted to have some kind-of-a AsyncResult (as it is well known to WIN32 developers) in JavaScript. I was very confident it will be a very simple but very clever and usefull javascript code.
Of course, I was somewhat wrong on the simplicity of developing this WIN32 idiom in JavaScript + DOM.
Necessary detour. Before some of you ‘acuse’ me of severe lack of knowledge, etc. I indeed know (and agree) that javascript does not do real multithreading. Same goes for the runtime environment used here, and that is DOM of course. But I also agree with everything conceptual in (for example) this article ( http://www.sitepoint.com/article/multi-threading-javascript ) . Are we ok now? We are? Ok then.
So, back to here and now. Here is my first version.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
function spawn ( FP , tu ) { var AsyncResult = function () { var tid = null ; function doit () { clearTimeout(tid); this.retval = FP(this); this.done = true ; } tid = setTimeout( doit , (tu == null ? 1 : tu) ) ; } // AsyncResult.prototype.done = false ; AsyncResult.prototype.retval = null ; AsyncResult.prototype.toString = function () { return "{ retval : " + this.retval + ", done : " + this.done + " }"; } // return new AsyncResult() ; } // function worker () { return "worker has finished"; } // spawn( worker); /* REZULT: { retval : null, done : false } */ |
Have you expected this result? I will ‘reveal’ at once that the issue is that “this” inside the function doit() points to the window object. It does not point to the instance of the AsyncResult.
So why is this == window, inside doit() ? Also, this was happening inside the HTA program I used to test. Might this be the reason ? And how to assign to the AsyncResult, public properties ‘retval’ and ‘done’? From inside its private method doit() ? Was I doing it right?
Or is here some more fundamental issue presenting itself ? Maybe if I do the version with private properties, all will be magically solved? So here is my second attempt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/*------------------------------------*/ function spawn ( FP , tu ) { var AsyncResult = function () { var done = false ; this.isDone = function () { return done; } var retval = null ; this.rezult = function () { return retval; } this.toString = function () { return "{ retval : " + retval + ", done : " + done + " }"; } var tid = null ; function doit () { retval = FP(this); done = true ; clearTimeout(tid); } tid = setTimeout( doit , (tu == null ? 1 : tu) ) ; } return new AsyncResult() ; } // function worker () { return "worker has finished"; } // spawn( worker, 1000 ); /* REZULT: { retval : null, done : false } */ |
Also coded in a different, more condensed, paradigm of JS class declaration. Without using the ‘prototype’ feature. But alas, RESULT is still the same ?! Even with ‘this’ keyword, moved out of the equation.
How is this possible, what can we conclude here? Well I have quickly found that ‘done’ and ‘true’ are still introduced to the global space (actually added to the window object) even in this version. After short thinking, I concluded this can mean only one thing. setTimeout()
executes doit()
outside of the context of its immediate parent. It executes doit()
in the context of the window object! So the next version, without setTimeout()
, is here :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function spawn ( FP , tu ) { var AsyncResult = function () { var done = false ; this.isDone = function () { return done; } var retval = null ; this.rezult = function () { return retval; } this.toString = function () { return "{ retval : " + retval + ", done : " + done + " }"; } function doit () { retval = FP(this); done = true ; } doit(); } return new AsyncResult() ; } // function worker () { return "worker has finished"; } // spawn( worker ); /* { retval : worker has finished, done : true } */ |
And indeed result is what we expected it to be. But with the whole starting idea in “ruins”. The code executes immediately. There is no any “spawning”. No even fake “multi threading” etc.. The call FP(this), inside the AsyncResult, may block for a long time.
What should I do? Some investigation, that much is obvious. Ian Smith, also sent me this link http://www.kryogenix.org/code/browser/secrets-of-javascript-closures/, where I have learnt (again?) about ‘that’. Or how to preserver ‘this’ as ‘that’, to be used inside closures.
So in essence my doit()
method will have access to the current instance of AsyncResult
through ‘that’, because ‘this’ is a ‘window’ object, because doit()
gets executed by setTimeout()
, So here we go: the next version :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var AsyncResult = function (FP,tu) { var done = false ; var retval = null ; this.toString = function () { return "{ retval : " + retval + ", done : " + done + " }"; } var tid = null ; var that = this ; // keep the current instance function doit () { clearTimeout(that.tid); // use the current instance that.retval = FP(that); that.done = true ; } tid = setTimeout( "that.doit()" , (tu == null ? 1 : tu) ) ; } function spawn ( FP , tu ) { return new AsyncResult(FP,tu) ; } // function worker () { return "worker has finished"; } // spawn( worker, 1000 ); |
And also AsyncResult()
is not a closure any more, because of many FUD messages about IE’s inability to live with javascript closures.
(FUD = Fear Uncertainty and Doubt). And the result, what is the result ? Still the same..
1 |
{ retval : null, done : false } |
And also an exception from the javascript engine saying : “Line 0, ‘that’ is undefined” . Which is is obviously coming from this line :
1 |
tid = setTimeout( "that.doit()" , (tu == null ? 1 : tu) ) ; |
Which has to be rectifyied to look like this :
1 |
tid = setTimeout( doit , (tu == null ? 1 : tu) ) ; |
And all is dandy, besides the fact that we still have no result. And no AsyncResult, which is supposed to work and execute the worker “asynchronously” and keep the result.
1 2 3 4 5 6 |
// // wrong result, doit() is called but leaves // no trace on AsyncResult returned from the // spawn() function { retval : null, done : false } // |
Ok, not to despair. Let’s “take the walk”, and come back to this problem, with “fresh pair of eyes”.
2009-01-26
Ian sent me very comprehensive comment (see bellow). WIth lot of nice code, and a last two words of the comment: ” … normal JavaScript … ” . Ok, he is sincere at least ;)
I realise I am trying to emulate C++ thinking and paradigms in poor little JavaScript. There is a little known feature of C++ called Local Classes. You can define C++ class right inside a function :
1 2 3 4 5 6 7 |
// This is C++ void fun () { class Local { // class declaration and implementation } ; // code using Local } |
There are limitation, Local C++ classes can not define static member variables (aka class properties in JavaScript) and can not access non-static member variables. Nice little encapsulation in practice (vs theory) useful example. In still the “top” programming language. Not to get scared, let us get back to our JavaScript issue at hand.
I wanted to convince myself that doit() inside AsyncResult gets called and executed, so I (rather weakly ?) resorted to good old alert()
.
Here is the doit()
variant with alert()
inside:
1 2 3 4 5 6 |
// ... function doit () { clearTimeout(that.tid); that.retval = FP(that); that.done = true ; alert(that); } // ... |
Since ‘that’ is actually a current instance of the AsyncResult, ‘alert(that)’ will call a toString() method on it and show us its current state of ‘retval’ and ‘done’ properties. And? Well “still no baby”, result is still the same :
1 |
{ retval : null, done : false } |
How is this possible? Which ‘that’ is this then (sic) ? We just changed it’s properties, and we called it’s toString() in the very next statement, and still we see no change ?
This can mean that ‘that’ I am using/seeing inside doit() is not the same ‘that’ I have made before it with :
1 |
var that = this; |
I have quickly added tid, to my toString(). And produced yet another AsyncResult version :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var AsyncResult = function (FP,tu) { var done = false ; var retval = null ; this.toString = function () { return "{ retval : " + retval + ", done : " + done + ", tid: " + tid + " }"; }; var tid = null ; var that = this ; function doit () { that.retval = FP(that); that.done = true ; alert( that.tid +"\n" + that); clearInterval(that.tid) ; } that.tid = setTimeout( doit , (tu == null ? 1 : tu) ) ; } function spawn ( FP , tu ) { return new AsyncResult(FP,tu) ; } // function worker () { return "worker has finished"; } // spawn( worker, 1000 ); // Result : // { retval : null, done : false, tid: null } // |
Now the result is still “wrong” with added information telling as that “timer ID” aka “tid” is also not the “right one” ?!
So doit()
executes in some “quantum space” which is “nowhere”. Or as quantum mechanics are saying: “Where, is the wrong question to ask” …
Then I spotted a bug. In the last version above, I have ‘that.tid’ variable and I have ‘var tid’ variable. And this is a obvious mistake: they are not the same variables.
After ‘that.tid = SetTimeout(...
‘ line, that.tid
and this.tid
will be the same variables. But not the same as ‘var tid’ which is private to the AsyncResult
.
So in my toString()
method I am using private variables, while in the doit()
method I am using variables that do not exist before I make them! Therefore this line :
1 |
that.retval = FP(that); that.done = true ; |
Creates new properties on the ‘that’ object. Which is actually ‘this’. It is NOT, using the private variables of the AsyncResult
, at all! This is why they stay “untouched”. And this is how we do see them after calling that.toString()
: just plain unchanged.
Ok, back to code, Might this be the next and the final version then?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// V 6 function spawn ( FP , tu ) { var AsyncResult = function (FP,tu) { this.toString = function () { // return JSON formated object exposing private properties // this pattern exposed private properties without get/set methods return "{ retval : " + that.retval + ", done : " + that.done + " }"; } // preserve the current instance aka 'this' var that = this ; // create properties on the curent instance that.done = false ; that.retval = null ; that.tid = null ; // this private method is delay executed *outside* of the context // of the current instance // it is executed in the context of the 'global space' // aka the 'window' object function doit (x) { try { // execute FP() and preserve its return value that.retval = FP(); // signal that execution finished ok that.done = true ; } catch (x) { // FP() caused an exception that.retval = x ; // for caller to see which one // signal that execution finished in errror that.done = false ; } // for testing purposes show the state of the AsyncResult instance alert( 'AsyncResult::doit()\n\n' + that); // use clearInterval to release the timer ID clearTimeout(that.tid) ; } // delay doit() execution , in the context of the 'window' object that.tid = setTimeout( doit , (tu == null ? 1 : tu) ) ; } return new AsyncResult(FP,tu) ; // immedate instance return // FP() to be delay-executed in 'tu' microseconds } // function worker () { return "worker has finished"; } // spawn( worker, 1000 ); |
The immediate result returned from spawn()
, is as expected :
1 |
{ retval : null, done : false } |
But, after 1000 microseconds (as requested), doit()
got executed, and than FP()
, in turn. And then I was greeted with a nice alert box saying :
1 |
{ retval : 'worker has finished' , done: true } |
Voila! Success at last. I have AsyncResult which works, as I wanted it to work in the first place.
It calls my function after requested amount of microseconds and it keeps the value returned.
This version catchess any exception made “inside” FP()
call. The next step would be to extend the whole design so that it can be called with arguments.
Elegantly, as ever, of course.
NOTE: I did not know about Mozilla ‘workers’ idea ( https://developer.mozilla.org/En/Using_DOM_workers) before today. Also it seems quite different from my little pattern.
3 thoughts on “Asynchronous JavaScript”
I’m not sure what you are trying to do is possible. It looks like you want to be able to spawn a function to run async but still return its result when it is done. If I understand that correctly then you probably need to use a callback function. In that case I think your code gets a lot simpler.
I played around with your code and finally ended up with this. The AsyncResult function expects a function that will return another function as its call back.
I think created an example function that will create and run two “workers” as well as use setInterval to display the workers status on the page. I added a quick div with the id of “result” to display the results inline to get around using alerts.
So in short the code spawns two workers and a third to monitor (display the status of) the two workers on the screen. You don’t have to have the third worker but that is to prove it is working. I don’t really know if this is what you are trying to do, but it is a way to use async in normal JavaScript.
I’m confused as to way you need to clearInterval. If it ran the function it is clear, no?
Thanks for this comment, Ian. And for the first one, of course, whith usefull code included.
First of all it is a bug. I should have called clearTimeout() and not clearInterval(). This is now rectified.
Second, “timer ID” (or ‘that.tid’ in the doit() above), is not cleared automatically. At least not in IE.
If one has a lot (ie hundreds) of calls to
setTimeout()
orsetInterval()
inside a single page, without matchingclearTimeout()
andclearInterval()
, then IE will become “unstable”. I am sure I had this issue, but I seem not to remeber when ;o(In any case none of the above is not tested in FF or Chrome. Yet.