Is this JavaScript serial (killer :)) ever going to finish ? Well I am still having a lot of fun with this little JavaScript mechanism.
For AsyncResult 2/3 please click here.
First of all I have streamlined the AsyncResult
itself , and even made it more future proof, by using proper JSON API, and future JSON names. Here is the very latest code:
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
// // AsynResult V 8.0 // MIT style (c) 2009-2010 by DBJ.ORG // caling example // spawn( // { 'worker': w1, // 'delay': 1000, // 'callback': on_async_finished }, // 1, 2, 3) // // Mandatory arguments : // 1:single JSON object where // worker [mandatory] function pointer of the worker // to execute asynchronously // delay [optional] delayed execution time unit in microseconds, // if null then 1. // callback [optional] called 'back' when worker is done // with AsyncResult instance // as its only argument // [optional] // any arguments required by a worker function // function spawn ( ){ var AsyncResult = function(args_) { var FP = args_[0].worker; var tu = parseInt(args_[0].tu === null ? 1 : args_[0].tu); tu = (tu > spawn.maxdelay ? spawn.maxdelay : tu) var CB = args_[0].callback; // create properties on the curent instance this.done = false; this.retval = null; this.tid = null; this.callback = CB; // preserve the current instance aka 'this' in 'that' var that = this; // transform arguments to array and cut the first element // thus leaving arguments for FP that.args = Array.prototype.slice.call(args_).slice(1); // be 'future proof use: http://www.json.org/json2.js this.toJSONString = function() { return JSON.stringify(that); } this.toString = function() { // return JSON formated object exposing private properties // NOTE: this idiom exposes private properties // without get/set methods return this.toJSONString(); } // 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(args), and preserve its return value // use 'that' for execution contest that.retval = FP.apply(that, that.args); // signal that execution finished ok that.done = true; } catch (x) { // FP() caused an exception that.retval = x.message; // 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 if (spawn.log) spawn.log("AsyncResult::doit()\n\n" + that); // use clearInterval to release the timer ID clearTimeout(that.tid); // use the callback , if given if (that.callback) { var cb_tid = setTimeout(function(x) { clearTimeout(cb_tid); // pass 'that'/'this' to the callback try { that.callback(that); } catch (x) { /* ignore callback exceptions, but log them */ if (spawn.log) spawn.log("AsyncResult::doit() callback exception\n\n" + x.message); }}, 1); } } // delay doit() execution , in the context of the 'window' object that.tid = setTimeout(doit, tu); } return new AsyncResult( arguments ) ; // immediate instance return } // maximum number of seconds spawn will accept spawn.maxdelay = 60 * 1000; // is one minute // assign a logging method. if null will not be used // otherwise must be a function with a single string argument spawn.log = null ; |
Here and now, you might ask yourself a question : “Will I feel lucky, if I use this ?” . After all, ( for you too ) it is sometimes easier to “reinvent a hot water”, than to use other peoples “traps”.
In order to defend and promote my AsyncResult
mechanism, I have made one certainly non-trivial example. A famous large-image download mechanism. Here I am actually using a well known jQuery mechanism to do it, but with the “twist”: it is used through my AsyncResult
mechanism.
Why? To give more control to you.
How? By giving you one more intermediary event (or step), which happens while browser is waiting for large image to arrive. This you use to show that famous spinning gif, in order to improve “end user experience”.
Can this be done without AsyncResult
? Certainly. But in a less encapsulated and less decoupled way.
Here is the testing js FIDDLE for you to try immediately.
For quicker understanding one should really start reading from the end. On calling spawn(), immediately an AsyncResult
instance is returned and its toString()
method is called, since we are here using it from inside a call to the dbj.log( “…some string …”) method. This is the jist of the software mechanism. Its perceived usage. This is where API succeeds or fails.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* */ dbj.log("Async started, spawn() returned : " + spawn( /* first argument is mandatory JSON object */ { 'worker': begin_async_operation, 'delay': 1, 'callback': end_async_operation }, /* all the other args are optional. in this case we pass the image source url and the callback to be used by jQuery inside load(), on image ready event */ large_image_url, on_large_image_loaded ) ); |
Above, jQuery is extensively used. I hope code is well documented and explained. Enjoy…
2 thoughts on “AsyncResult 3/3”
Very nice work. I’ll have to play around with this on my next project.
@Ian,
As much as I am thankful for this, I am now under pressure.
Once users kick-in, there is no way back. Everything suddenly becomes “cast in stone, sign in blood”. Every decision made will be rewarded or will “come back to hit me”.
So, thanks Ian, you just made me work more :)