Async, Callbacks, and OOP JavaScript: how do I organize this? - javascript

I'm building a plugin that fetches info for a bunch of images in JSON, then displays them in some dialog for selection. Unfortunately, my first intuition pretty clearly results in a race condition:
var ImageDialog = function () {};
ImageDialog.prototype.items = [];
ImageDialog.prototype.fetch_images() {
var parse_images = function(data) {
// Magically parse these suckers.
data = awesome_function(data);
this.items = data;
};
magicalxhrclass.xhr.send({"url": 'someurl', "success": parse_images, "success_scope": this});
}
ImageDialog.prototype.render = function () {
this.fetch_images();
// XHR may or may not have finished yet...
this.display_images();
this.do_other_stuff();
};
var monkey = new ImageDialog();
monkey.render();
Off of the top of my head, I think I could fix this by changing the parse_images callback to include the rest of the render steps. However, that doesn't look quite right. Why would the fetch_images method be calling a bunch of things about displaying images?
So: what should I do here?
I am pretty certain deferreds would help, but alas: I need to write this without any external libraries. :(
Comments on other code smells would be nice, too!

In general, the basic idea you can use is that when a regular program would use a return statement (meaning, "My function is done now do your job!") an asynchronous continuation-passing program would instead use a ballcabk function that gets explicitly called
function fetch_images(callback){
magicalXHR({
success: function(data){
parse_images(data);
callback(whatever);
}
}
}
or, if parse_images is itself an async function:
parse_images(data, callback)
Now when you call fetch_images the code after it goes into a callback instead of assuming that fetch_images will be done when it returns
fetch_images(function(
display_images()
})
By using callbacks you can emulate pretty well what a traditional program could do (in fact its a fairly mechanical translation between one form of the other). The only problem you will now encounter is that error handling gets tricky, language features like loops don't play well with async callbacks and calbacks tend to nest into callback hell. If the callbacks start getting too complex, I would investigate using one of those Javascript dialects that compiles down to continuation-passing-style Javascrit (some of them work without needing extra libraries at runtime).

How about this?
var ImageDialog = function () {
this.items = []; // just in case you need it before the images are fetched
};
ImageDialog.prototype.fetch_images(callback) {
var that = this;
function parse_images (data) {
// Magically parse these suckers.
data = awesome_function(data);
that.items = data;
callback.apply(that);
};
magicalxhrclass.xhr.send({"url": 'someurl', "success": parse_images, "success_scope": this});
}
ImageDialog.prototype.render = function () {
this.fetch_images(function(){
this.display_images();
this.do_other_stuff();
});
};
var monkey = new ImageDialog();
monkey.render();

Here's a thought about what to do.
ImageDialog.prototype.fetch_images() {
var parse_images = function(data) {
// Magically parse these suckers.
data = awesome_function(data);
this.items = data;
fetch_images.caller() // Unfortunately, this is nonstandard/not in the spec. :(
};
magicalxhrclass.xhr.send({"url": 'someurl', "success": parse_images, "success_scope": this});
}
ImageDialog.prototype.render = function () {
if (this.items === []) {
this.fetch_images()
return;
} else {
this.display_images();
this.do_other_stuff();
};
};
This way I'm not passing some implementation detail to fetch_images, and I get caching, to boot. Am I still trying too hard to escape CPS, or is this sensible?

Related

What is the best method for loading in dependencies & instantiating object only once in Javascript?

I'd like to explore battle-tested, proven design patterns in Javascript that ensure loading in dependencies only once (if they were already loaded in) & instantiates an object only once as well in the DOM.
Specifically, i have following:
// Block A in the DOM
<script>
//instantiate my golden object as follows
var some_unique_options = {
"option1": "some-val",
"option2": "some-val"
}
$.ajaxSetup({cache:true});
$.getScript('my_golden_script.js', function(){
golden_object.init(some_unique_options);
});
</script>
// Block B in the DOM
<script>
//instantiate my golden object as follows
var another_unique_options ={
"option1": "some-val",
"option2": "some-val"
}
$.ajaxSetup({cache:true});
$.getScript('my_golden_script.js', function(){
golden_object.init(another_unique_options);
});
</script>
Currently, my golden_object implements a singleton as per: http://robdodson.me/javascript-design-patterns-singleton/
Intended behavior here is that, when the above Bock A & B execute top to bottom in the DOM, golden_object.init in Block B would leverage resources defined during the operation of Block A. Business requirement is as such that Block A & B would be identical, and there could be more in the DOM. After several days of researching, I'm still unable to come up with a solid structure. My singleton keeps re-creating itself as new, and only if i call golden_object.init(); in the devtools console, it would use the existing object in the DOM. How can i get this to work the way it's supposed to? what/where am i making a mistake here?
FYI, golden_object.js does carry $.fn.someOtherObj & couple of dependencies via $.getScript() as well as the definition of the singleton (i wanted Singleton obj to be user interface)
Million thanks to your insights & suggestions!
You could create a wrapper around jQuery.getScript that ensures the script is loaded only once, and, in the case of the second and further instances, calls the callback once the first script has loaded
jQuery.getScriptOnce = (function($) {
var urls = {};
return function(url, callback) {
var obj = urls[url];
if (!obj) {
urls[url] = new Promise(function(resolve, reject) {
$.getScript(url, function(script, textStatus, jqXHR) {
resolve({script: script, textStatus: textStatus, jqXHR: jqXHR});
callback(script, textStatus, jqXHR);
});
});
} else {
obj.then(function(val) {
callback(val.script, val.textStatus, val.jqXHR);
});
}
};
})(jQuery);
this has the benefit of only needing to change all occurrences of $.getScript to $.getScriptOnce
Note: I haven't checked to see if jQuery.getScript returns a jQuery promise, if it does, then the code can be a lot simpler
Just tested and it seems getScript DOES return a jquery promise - so you can simply do
jQuery.getScriptOnce = (function($) {
var urls = {};
return function(url) {
return urls[url] = urls[url] || $.getScript(url);
};
})(jQuery);
but you'll change
$.getScript('my_golden_script.js', function(){
golden_object.init(some_unique_options);
});
to
$.getScriptOnce('my_golden_script.js').then(function(){
golden_object.init(some_unique_options);
});

Javascript/jQuery object scope issues

I am having some issues with scope in JS, which I am just picking up.
I've defined an object and am making a .getJSON() call within it, but I don't seem to be able to correctly refer to the calling object's attributes:
// Vehicle object
function vehicle(id) {
this.id = id;
var that = this;
// Fetch some JSON
$.getJSON("json.php?act=vehicleInfo&id=" + this.id, function (json) {
that.vehicleInfo = json
that.icon = L.AwesomeMarkers.icon({ icon: that.vehicleInfo.icon, color: that.vehicleInfo.colour });
that.polyline = new L.Polyline([[that.vehicleInfo.latitude, that.vehicleInfo.longitude]]);
that.marker = L.marker([that.vehicleInfo.latitude, that.vehicleInfo.longitude], {icon: that.icon});
that.marker.bindPopup("Test point");
that.marker.addTo(map);
that.polyline.addTo(map);
});
}
// Vehicle move method
vehicle.prototype.move = function(latlng){
this.marker.setLatLng(latlng);
this.polyline.addLatLng(latlng);
}
When I call .move(), this.marker is undefined. Where am I going wrong here?
Unfortunately, Ajax doesn't work that way. You can't depend on the $.getJSON callback completing at any particular time or even at all. One possibility is to make the request synchronous, but this is not recommended because it locks up the browser.
The only two solutions that are possible are:
Do not depend on ajax
Make anything that depends on the result of an ajax callback depend on the callback itself.
That is any code that calls .move for a vehicle has to be done as a result of the $.getJSON call. You can make it look a bit more elegant, though:
this.jqxhr = $.getJSON(...
/* snip */
vehicle.prototype.move = function (latlng) {
var veh = this;
this.jqxhr.done(function () {
veh.marker.setLatLng(latlng);
veh.polyline.setLatLng(latlng);
});
}

can I emulate a C-like array of pointers in javascript?

I'd like to be able to store the addresses of a bunch of different variables in an array. This allows me to access the variables by name or iterate through them if I need to. Is this possible in JS?
(function(ns){
ns.obj = new function(){
var foo = "foo";
var bar = "bar";
//i really want this:
//var ary = [&foo, &bar];
var ary = [foo, bar];
this.print = function() {
console.log( foo );
console.log( bar );
}
this.setFoo = function( newFoo ) {
//i really want this:
//*(ary[0]) = newFoo;
ary[0] = newFoo;
}
this.printAry = function() {
for( var i=0; i < ary.length; ++i ) {
console.log( ary[i] );
}
}
};
}(window.ns = window.ns || {}) );
ns.obj.print();
ns.obj.setFoo("newfoo!");
ns.obj.printAry();
ns.obj.print();
I looked at this:
JavaScript array of pointers like in C++
But I'd like to be able to use an element of ary on the LHS of an assignment and I don't think that example works in this situation.
WHY ON EARTH DO I WANT TO DO THIS?
A lot of comments so far have (rightfully) asked why I'd want to do this. I'm dealing with a proprietary API that involves an asynchronous object initialization mechanism. Basically I create an instance of an object and then pass it to this initializer to be able to actually use it. The initializer includes a field for an onSuccess handler to notify of successful initialization. My fully initialized object is passed as an argument into this success handler so that I can grab a reference to it.
I'm then free to initialize my next object. It looks kinda like this:
var a = new api.ApiObject();
var b = new api.ApiObject();
var c = new api.ApiObject();
var d = new api.ApiObject();
//omg this is ugly
api.initializeObject( {
objToInit: a,
onSuccess: function(args) {
a = args.obj;
api.initializeObject( {
objToInit: b,
onSuccess: function(args) {
b = args.obj;
api.initializeObject( {
objToInit: c,
onSuccess: function(args) {
c = args.obj;
api.initializeObject( {
objToInit: d,
onSuccess: function(args) {
d = args.obj;
}
} );
}
} );
}
} );
}
} );
a.doCoolStuff();
//and so on
This deeply nested mess just gets worse as I add more api.ApiObjects(). So what do I do to fix this? I can't change the API, but maybe a recursive function could help:
//maybe a recursive function could make this more concise?
function doInitialize( ary ) {
api.initializeObject( {
objToInit: ary[0];
onSuccess: function(args) {
//i'd like to assign this passed in reference to my local
//reference outside this function (var a, b, etc).
//An array of pointers would be useful here.
//how else can I get this assigned out, cuz this doesn't work...
ary[0] = args.obj;
if( ary.length > 1 ) {
ary.splice( 0, 1 );
doInitialize( ary );
}
}
}
}
doInitialize( [a,b,c,d] );
//this won't work because I don't have a reference to the fully initialized object
a.doCoolStuff();
So maybe the better question is: is there an established pattern to deal with asynchronous success chaining like this? I think I've seen other public JS frameworks (like dojo) use this sort of onSuccess chaining... how do I make this not ugly?
I might suggest that if your primary purpose for this is convenience as regards nesting of asynchronous callbacks, that you should consider a deferred/promise system.
I've written a couple of different promise libraries by hand.
jQuery comes with one built in (as do most "ajax libraries").
Here's what this might look like, in a better world:
doThingOne()
.then(doThingTwo)
.then(doThingThree)
.then(launch);
Assuming that doThingOne returns a promise.
A more familiar looking interface for people who use jQuery (or most other promise-using large libraries), might look like this:
var imageLoader = $.Deferred(),
loading = imageLoader.promise();
loading
.done(gallery.render.bind(gallery))
.done(gallery.show.bind(gallery));
var img = new Image(),
url = "...";
img.onload = function () { imageLoader.resolve(img); };
img.onerror = function () { imageLoader.reject("error message"); };
img.src = url;
Very basically, the Deferred above will hold two private arrays (one for "success", one for "failure"), and will extend an interface which allows the async part of the application to "succeed" or "fail", and will pass in whatever is chosen to be data/a callback/etc.
It also extends a promise method, which returns a promise object, containing subscription functions for the two private arrays. So you pass the promise object around to interested parties, and they subscribe callbacks to be iterated through, on success/failure of the async operation (and passed anything which is passed to the .resolve/.reject method of the operation).
This might seem like an inversion or extension of just adding a custom-event/listener/etc...
And it is.
The benefit of the abstraction is that the interface is cleaner.
Hiding this stuff inside of object interfaces, and just passing async promise-objects around can make your code look 100% synchronous:
var images = ImageLoader(),
gallery = ImageGallery(),
photo;
photo = images.load("//url.com/image.png"); // assuming `.load` returns a promise object
gallery.show(photo); // just a promise object, but internally,
//`.show` would subscribe a private method to the promise object
And doing things like having three separate async operations, which can arrive in any order, but must all be successful before advancing, then you can have something like this (again jQuery, but doing it by hand is possible, too).
$.when(promise_obj_1, promise_obj_2, promise_obj_3)
.done(nextPhase);
nextPhase, of course, being a callback which you anticipate to be fired if all three promises are successfully completed.
I'd be happy to provide implementation details for a barebones promise system, if you're like me, and don't like using different libraries without first understanding how each piece works on its own, and being able to replicate its functionality, without copying code.
The answer to the first part of your question is to use an object. You're thinking in C which doesn't have iteratable structs so C programmers reach for arrays. In JS objects are iteratable. So you should write it as:
ary = {
foo : 'foo',
bar : 'bar'
}
Or if we look at your second example:
var apis = {
a : new api.ApiObject(),
b : new api.ApiObject(),
c : new api.ApiObject(),
d : new api.ApiObject()
}
Now, as for the second part of your question. Your pseudo recursive code (pseudo because it's not really recursive in the stack sense since it's async) will now work with the apis object above. But you pass the keys instead of the object:
doInitialize( ['a','b','c','d'] );
Obviously, the bit above can be done dynamically by iterating through the apis object. Anyway, in the onSuccess part of the code you assign the result like this:
apis[ary[0]] = args.obj;
Oh, and obviously the objToInit should now be apis[ary[0]].
Now doing this should work as you expect:
apis.a.doCoolStuff();

Variable scope in Javascript Object

I'm discovering the concept of "objects" in JavaScript. I'm making an RSS Parser, and I have an error (commented).
function MyParser (feed_url) { // Construct
"use strict";
this.feedUrl = feed_url;
this.pubArray = [];
if (typeof (this.init_ok) == 'undefined') {
MyParser.prototype.parse = function () {
"use strict";
var thisObj = this;
$.get(this.feedUrl, function (data, textStatus, jqXHR) {
if (textStatus == 'success') {
var xml = jqXHR.responseXML,
//lastBuildDate = new Date($(xml).find('lastBuildDate').text());
items = $(xml).find('item');
items.each(function () {
var pubSingle = thisObj.makeObj($(this).find('pubDate').text(),
$(this).find('link').text(),
$(this).find('title').text(),
$(this).find('description').text(),
$(this).find('encoded').text(),
$(this).find('commentRss').text(),
$(this).find('comments').last().text());
thisObj.pubArray.push(pubSingle);
});
console.log(thisObj.pubArray); // OK
}
}, 'xml');
console.log(this.pubArray); // Empty
return (this.pubArray);
};
MyParser.prototype.makeObj = function (pubDate, pubLink, pubTitle, pubDesc, pubContent, pubComCount, pubComLink) {
"use strict";
var pubSingle = {};
pubSingle.pubDate = new Date(pubDate);
pubSingle.pubLink = pubLink;
pubSingle.pubTitle = pubTitle;
pubSingle.pubDesc = pubDesc;
pubSingle.pubContent = pubContent;
pubSingle.pubComCount = pubComCount;
pubSingle.pubComLink = pubComLink;
return (pubSingle);
};
}
this.init_ok = true;
}
If you look at the console.log(), you'll see that the line // OK is outputting my array correctly.
But later, when returning from $.get, my array is empty.
Does anybody have an idea why, and how to correct that please?
This is not a problem with variable-scope. The problem here is that you're working with asynchronous flow and you're not thinking correctly the flow.
Let me explain:
When you do your .get, you fire a parallel asynchronous process that will request information from the browser, but your main program's flow keeps going, so when you get to your "return" statement, your array has not been filled yet with the response from your get method.
You should use your array from inside the get callback and not outside of it, since you can't guarantee that the array will have the information you need.
Does it make any sense?
Let me know!
Further explanation
According to your comments, you're still doing something like this:
var results = MyParser(feed_url);
//code that uses results.pubArray
And you cannot do that. Even though you're setting your "pubArray" inside your .get callback, you're trying to use pubArray right after you called MyParser and that's before the .get callback is called.
What you have to do, is call your next step on your program's logic from within the .get callback... that's the only way you can be sure that the pubArray is filled with proper data.
I hope that makes it clearer.
This is because your line
console.log(this.pubArray); // Empty
is being called directly after you issue your Ajax request; it hasn't had time to fetch the data yet. The line
console.log(thisObj.pubArray); // OK
is being called inside the Ajax callback, by which time the data has been fetched.
Thank you all, and particulary #Deleteman .
Here is what I did:
$.get(this.feedUrl, 'xml').success(function () {
thisObj.handleAjax(arguments[0], arguments[1], arguments[2]);
$(document).trigger('MyParserDone');
}).error(function () {
$(document).trigger('MyParserFailed');
});
Then, when i enter "HandleAjax", i'm back in my object context, so "this" refers to my object and the right properties. The only "problem" is that I have to set a listener (MyParserDone) to make sure the parsing is finished.

Titanium mvc - call function and wait for result

I am currently in the process of making my first Titanium iPhone app.
In a model I got:
(function() {
main.model = {};
main.model.getAlbums = function(_args) {
var loader = Titanium.Network.createHTTPClient();
loader.open("GET", "http://someurl.json");
// Runs the function when the data is ready for us to process
loader.onload = function() {
// Evaluate the JSON
var albums = eval('('+this.responseText+')');
//alert(albums.length);
return albums;
};
// Send the HTTP request
loader.send();
};
})();
and I call this function in a view like:
(function() {
main.ui.createAlbumsWindow = function(_args) {
var albumsWindow = Titanium.UI.createWindow({
title:'Albums',
backgroundColor:'#000'
});
var albums = main.model.getAlbums();
alert(albums);
return albumsWindow;
};
})();
however it seems like the call to the model (which fetches some data using HTTP) doesn't wait for a response. In the view when I do the alert it haven't received the data from the model yet. How do I do this in a best-practice way?
Thanks in advance
OK,
Something like this,
function foo(arg1, callback){
arg1 += 10;
....
... Your web service code
....
callback(arg1); // you can have your response instead of arg1
}
you will call this function like this,
foo (arg1, function(returnedParameter){
alert(returnedParameter); // here you will get your response which was returned in above function using this line .... callback(arg1);
});
so here arg1 is parameter (simple parameter like integer, string etc ... ) and second argument is your call back function.
Cheers.
What you need is Synchronous call to web service, so that it will wait till you get the response from the service.
To achieve this in java script you have to pass callback function as parameter and get the return value in callback function instead of returning value by return statement.
Actually coding style you are using is new for me because i am using different coding style.
But the main thing is you have to use call back function to retrieve value instead of return statement. Try this and if you still face the problem than tell me i will try to give an example.
the callback way like zero explained is nicely explained, but you could also try to get it handled with events.
(function() {
main.ui.createAlbumsWindow = function(_args) {
var albumsWindow = Titanium.UI.createWindow({
title:'Albums',
backgroundColor:'#000'
});
var status = new object(), // eventlistener
got_a_valid_result = false;
// catch result
status.addEventListener('gotResult',function(e){
alert(e.result);
got_a_valid_result = true;
});
// catch error
status.addEventListener('error',function(e){
alert("error occured: "+e.errorcode);
git_a_valid_result = true;
});
var albums = main.model.getAlbums(status);
// wait for result
while (!got_a_valid_result){};
return albumsWindow;
};
})();
and your model may something like
main.model.getAlbums = function(status) {
var loader = Titanium.Network.createHTTPClient();
loader.open("GET", "http://someurl.json");
loader.onload = function() {
var albums = eval('('+this.responseText+')');
status.fireEvent('gotResult',{result:albums});
return albums;
};
loader.onerror = function(e){
status.fireEvent('error',{errorcode:"an error occured"});
};
// Send the HTTP request
loader.send();
};
Just as a suggestion, try to use JSON.parse instead of eval as there are risks involved with using eval since it runs all javascript code.
I think that the solution The Zero posted is likely better for memory management, but I'm not totally sure. If you do and eventListener, be aware of the following
(see https://wiki.appcelerator.org/display/guides/Managing+Memory+and+Finding+Leaks)
function doSomething(_event) {
var foo = bar;
}
// adding this event listener causes a memory leak
// as references remain valid as long as the app is running
Ti.App.addEventListener('bad:idea', doSomething);
// you can plug this leak by removing the event listener, for example when the window is closed
thisWindow.addEventListener('close', function() {
// to remove an event listener, you must use the exact same function signature
// as when the listener was added
Ti.App.removeEventListener('bad:idea', doSomething);
});

Categories

Resources