Join 2 'threads' in javascript - javascript

If I have an ajax call off fetching (with a callback) and then some other code running in the meantime. How can I have a third function that will be called when both of the first 2 are done. I'm sure it is easy with polling (setTimeout and then check some variables) but I'd rather a callback.
Is it possible?

You could just give the same callback to both your AJAX call and your other code running in the meantime, use a variable to track their combined progress, then link them to a callback like below:
// Each time you start a call, increment this by one
var counter = 0;
var callback = function() {
counter--;
if (counter == 0) {
// Execute code you wanted to do once both threads are finished.
}
}

Daniel's solution is the proper one. I took it and added some extra code so you don't have to think too much ;)
function createNotifier() {
var counter = 2;
return function() {
if (--counter == 0) {
// do stuff
}
};
}
var notify = createNotifier();
var later = function() {
var done = false;
// do stuff and set done to true if you're done
if (done) {
notify();
}
};
function doAjaxCall(notify) {
var ajaxCallback = function() {
// Respond to the AJAX callback here
// Notify that the Ajax callback is done
notify();
};
// Here you perform the AJAX call action
}
setInterval(later, 200);
doAjaxCall(notify);

The best approach to this is to take advantage of the fact that functions are first-order objects in JavaScript. Therefore you can assign them to variables and invoke them through the variable, changing the function that the variable refers to as needed.
For example:
function firstCallback() {
// the first thing has happened
// so when the next thing happens, we want to do stuff
callback = secondCallback;
}
function secondCallback() {
// do stuff now both things have happened
}
var callback = firstCallback;
If both your pieces of code now use the variable to call the function:
callback();
then whichever one executes first will call the firstCallback, which changes the variable to point to the secondCallback, and so that will be called by whichever executes second.
However your phrasing of the question implies that this may all be unnecessary, as it sounds like you are making an Ajax request and then continuing processing. As JavaScript interpreters are single-threaded, the Ajax callback will never be executed until the main body of code that made the request has finished executing anyway, even if that is long after the response has been received.
In case that isn't your situation, I've created a working example on my site; view the source to see the code (just before the </body> tag). It makes a request which is delayed by the server for a couple of seconds, then a request which receives an immediate response. The second request's response is handled by one function, and the first request's response is later handled by a different function, as the request that received a response first has changed the callback variable to refer to the second function.

You are talking about a thing called deferred in javascript as #Chris Conway mentioned above. Similarly jQuery also has Deferred since v1.5.
Check these Deferred.when() or deferred.done()
Don't forget to check jQuery doc.
But to give you some idea here is what I am copying from that site.
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively */
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
if ( /Whip It/.test(jqXHR.responseText) ) {
alert("First page has 'Whip It' somewhere.");
}
});
//Using deferred.then()
$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
.then(myFunc, myFailure);

Something like this (schematic):
registerThread() {
counter++;
}
unregisterThread() {
if (--counter == 0) fireEvent('some_user_event');
}
eventHandler_for_some_user_event() {
do_stuff();
}

You can do this easily with Google's Closure library, specifically goog.async.Deferred:
// Deferred is a container for an incomplete computation.
var ajaxFinished = goog.async.Deferred();
// ajaxCall is the asynchronous function we're calling.
ajaxCall( //args...,
function() { // callback
// Process the results...
ajaxFinished.callback(); // Signal completion
}
);
// Do other stuff...
// Wait for the callback completion before proceeding
goog.async.when(ajaxFinished, function() {
// Do the rest of the stuff...
});
You can join multiple asynchronous computations using awaitDeferred, chainDeferred, or goog.async.DeferredList.

Related

Mixing sync and async javascript/jquery and getting a success function at the end

Wondering what the best solution to this problem is, also this is not my actual code structure or names but the simplest way to illustrate the problem.
I have a function which was purely used to perform an ajax call and load a template with jquery.
function load(template) {
$('#container').load(template, data, function() {
// complete code here
});
}
Focusing on the 3rd param in $.load(), namely a callback function that runs when the request is complete.
Now I have my load() function in another wrapper function:
function processTask(variable) {
load(variable);
}
The problem I have is I need some code to run after the ajax load is complete, however as my app has grown my wrapper function processTask may or may not invoke an ajax load so I can't perform my must needed code inside the complete callback.
Do I change my $.load() to perform synchronous or just manage my code better so that if I am calling a $.load() it puts my needed code in the callback and if not it places it where I need it to be?
I have read about javascript Promises and I'm unsure if they will help in this situation.
EDIT
So my processTask is an object method.
function classObj(name, fn) {
this.name = name;
this.processTask = fn;
this.load = function(template) {
$('#container').load(template, data, function() {
// complete code here
});
}
}
And in context I do this:
var task = new classObj('taskName', function() {
this.load('myFile.php');
// Or another function and not load() based on whats needed in the task.
});
Basically I have an object that I can add custom methods to at will and they can easily be called dynamically, until now they have always loaded a file.
First, change your load function to return the xhr from get (or ajax):
function load(template) {
return $.get('myFile.php', data, function(result) {
$('#container').html(result);
});
}
Then, within your code you can use when then to perform your code after the load completes if applicable:
var xhr;
/* ... */
if(something){
xhr = load(template);
}
/* ... */
if(xhr){
$.when(xhr).then(doSomething);
} else {
doSomething();
}
And in fact, this can be simplified using the fact that a non-deferred object passed to when (including undefined apparently) will execute the then immediately and get rid of the if:
$.when(xhr).then(doSomething);
If xhr is undefined then when will resolve immediately causing then to execute immediately.

Elegant solution to conditional AJAX call

I am writing some javascript that includes a series AJAX calls and I am looking for an elegant solution to the following issue: The goal of the script is to gather parameters and then execute an API call with these parameters. The very first time the call is executed there is one parameter that needs to be requested from the server - every subsequent call will use a stored value of this parameter. This is where the issue begins. I want a conditional AJAX call to be made only if this is the first time. I don't want to put the rest of the code into the success function of that AJAX call as that seems convoluted. I would like something like the following but due to the obvious asynchronous nature of the call I realize this is not possible. I also want to avoid having a synchronous call as this would cause the thread to block:
var myParameter;
if(!params.myParam.isStored) {
myParameter = getParamWithAjaxCall();
} else {
myParameter = params.myParam;
}
// Continue with the rest of execution here of which there is a lot of code
Sorry if this seems like an obvious question and I have looked into solutions using the following but I am looking for an experienced opinion on what the most elegant solution would be:
jQuery: when.done
jQuery: async: false
Passing a callback to the Ajax call
I would create a wrapper function which you pass your logic to as a callback in done(). Something like this:
function makeRequest(callback) {
if (!params.myParam) {
// retrieve param
$.ajax({
url: '/getParam',
success: function(data) {
params.myParam = data.param;
}
}).done(callback);
}
else {
// param already has a value...
callback();
}
}
makeRequest(function() {
// make your AJAX request here, knowing that params.myParam will have a value.
});
You could use promises like so (I have used JQuery promises here):
function ParameterValueProvider() {
var parameterValue;
return function() {
var deferred = $.Deferred();
if ( parameterValue === undefined ) {
$.ajax({
// ... ajax parameters go here
}).done(function(rsp) {
parameterValue = rsp;
deferred.resolve(parameterValue);
});
}
deferred.resolve(parameterValue);
return deferred;
}
}
// Your Application
(function() {
'use strict';
var getParam = ParameterValueProvider();
// this will get the value from server the firs time
// and subsequent calls will use the cached value
getParam().then(function() {
// subsequent ajax calls go here
});
}());

Wait for multiple deferred to complete [duplicate]

So, I have a page that loads and through jquery.get makes several requests to populate drop downs with their values.
$(function() {
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
LoadContact();
};
It then calls LoadContact(); Which does another call, and when it returns it populates all the fields on the form. The problem is that often, the dropdowns aren't all populated, and thus, it can't set them to the correct value.
What I need to be able to do, is somehow have LoadContact only execute once the other methods are complete and callbacks done executing.
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
Is there something in jQuery that allows me to say, "Execute this, when all of these are done."?
More Info
I am thinking something along these lines
$().executeAfter(
function () { // When these are done
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
},
LoadContact // Do this
);
...it would need to keep track of the ajax calls that happen during the execution of the methods, and when they are all complete, call LoadContact;
If I knew how to intercept ajax that are being made in that function, I could probably write a jQuery extension to do this.
My Solution
;(function($) {
$.fn.executeAfter = function(methods, callback) {
var stack = [];
var trackAjaxSend = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
stack.push(url);
}
var trackAjaxComplete = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
var index = jQuery.inArray(url, stack);
if (index >= 0) {
stack.splice(index, 1);
}
if (stack.length == 0) {
callback();
$this.unbind("ajaxComplete");
}
}
var $this = $(this);
$this.ajaxSend(trackAjaxSend)
$this.ajaxComplete(trackAjaxComplete)
methods();
$this.unbind("ajaxSend");
};
})(jQuery);
This binds to the ajaxSend event while the methods are being called and keeps a list of urls (need a better unique id though) that are called. It then unbinds from ajaxSend so only the requests we care about are tracked. It also binds to ajaxComplete and removes items from the stack as they return. When the stack reaches zero, it executes our callback, and unbinds the ajaxComplete event.
You can use .ajaxStop() like this:
$(function() {
$(document).ajaxStop(function() {
$(this).unbind("ajaxStop"); //prevent running again when other calls finish
LoadContact();
});
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
});
This will run when all current requests are finished then unbind itself so it doesn't run if future ajax calls in the page execute. Also, make sure to put it before your ajax calls, so it gets bound early enough, it's more important with .ajaxStart(), but best practice to do it with both.
Expanding on Tom Lianza's answer, $.when() is now a much better way to accomplish this than using .ajaxStop().
The only caveat is that you need to be sure the asynchronous methods you need to wait on return a Deferred object. Luckily jQuery ajax calls already do this by default. So to implement the scenario from the question, the methods that need to be waited on would look something like this:
function LoadCategories(argument){
var deferred = $.ajax({
// ajax setup
}).then(function(response){
// optional callback to handle this response
});
return deferred;
}
Then to call LoadContact() after all three ajax calls have returned and optionally executed their own individual callbacks:
// setting variables to emphasize that the functions must return deferred objects
var deferred1 = LoadCategories($('#Category'));
var deferred2 = LoadPositions($('#Position'));
var deferred3 = LoadDepartments($('#Department'));
$.when(deferred1, deferred2, deferred3).then(LoadContact);
If you're on Jquery 1.5 or later, I suspect the Deferred object is your best bet:
http://api.jquery.com/category/deferred-object/
The helper method, when, is also quite nice:
http://api.jquery.com/jQuery.when/
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
No need for setTimeout. You just check in each callback that all three lists are populated (or better setup a counter, increase it in each callback and wait till it's equal to 3) and then call LoadContact from callback. Seems pretty easy to me.
ajaxStop approach might work to, I'm just not very familiar with it.

Javascript - pause execution until flag becomes true

how can i pause a javascript execution until a flag becomes true?
For Example, i've a xml message like this:
[...]
<action>
<resource update>id</resourceupdate>
</action>
<action>
<event>id1</event>
</action>
<action>
<event>id2</event>
</action>
<action>
<event>id3</event>
</action>
[...]
I wish that the event nodes are processed only after processing node resourceupdate (which requires more time to be served, as it requires the loading of a page):
in javascript to process this message with an iterator (each) i've tried:
$(_response).find('ACTION').each(function() {
if (tagName=="RESOURCEUPDATE") {
ready = false;
//load the resource with selected id in an iframe
} else if (tagName=="EVENT") {
browserLoaded(); //the waiting function
eventhandler(); //consume the event
}
});
the waiting function is:
function browserLoaded() {
if (!ready) {
setTimeout(browserLoaded(),1000);
}
}
and the ready var becomes true when the iframe is loaded:
$(iframe).load(function() {
ready = true;
});
but when execute i'll catch this error:
Maximum call stack size exceeded error
any ideas?
thanks!
It's really a bad idea to use some kind of a flag. You have to use Deferred Pattern. Something like this:
var resources = [];
$(_response).find('ACTION').each(function() {
var deferred = resources.length > 0 ? resources[resources.length - 1] : null;
switch (tagName) {
case "RESOURCEUPDATE":
deferred = $.Deferred();
//load the resource with selected id in an iframe
$(iframe).bind('load', function () {
deferred.resolve(/*specific arg1, arg2, arg3, ...*/)
});
resources.push(deferred);
break;
case "EVENT":
if (deferred) {
deferred.done(function (/*specific arg1, arg2, arg3, ...*/) {
// process event node
});
}
break;
}
});
// clean up deferreds objects when all them will be processed
$.when.apply($, resources).then(function() {
resources.length = 0;
})
P.S.: http://api.jquery.com/category/deferred-object/
The problem is in this function which call itself until the stack is full:
function browserLoaded() {
if (!ready) {
//here you call browserLoaded function instead of passing a reference to the function
setTimeout(browserLoaded() ,1000);
}
}
Your function should look like this:
function browserLoaded() {
if (!ready) {
// note the missing "()"
setTimeout(browserLoaded, 1000);
}
}
This is a terrible design. You don't need the 'waiting' timeout mechanism. If you are loading the pages via jQuery ajax request, make use of the callback functions to continue with your code execution (you can perhaps keep track of the 'current' item being processed and continue with the next). If you are loading iFrames, that's bad design too, you should move to the jQuery ajax way.
One quick hack that you could do is just set up a polling loop: use setInterval to check every once in a while if the variable has been set and clearInterval and continue execution when its time.
Any way, its going to be a pain to do things. Essentially, the only way to tell something in Javascript to run latter is to package it inside a function. After you do this it gets easier though, since you can pass that function around and have the async code call it back when you are done.
For example, your processing might look something like this:
//this is a simple "semaphore" pattern:
var things_to_load_count = 1
var check_if_done(){
things_to_load_count--;
if(things_to_load_count <= 0){
//code to do stuff after you finish loading
}
};
$(_response).find('ACTION').each(function() {
if (tagName=="RESOURCEUPDATE") {
things_to_load_count++;
run_code_to_load_stuff( check_if_done )
//make sure that the run_code_to_load_stuff function
//calls the callback you passed it once its done.
} else if (tagName=="EVENT") {
//process one of the normal nodes
}
});
//this will run the remaining code if we loaded everything
//but will do nothing if we are still waiting.
check_if_done()
Are you sure that the ready variable that set true in the iframe load function is the same as the one that is checked before another settimeout is called. It seems that the one in the iframe load function is a local variable and the other one a global variable.
Or both ready variables are local.

Setting timer after an asynchronous call returns

I have an asynchronous Ajax function which runs a command string at the server side and returns the result to the client. It calls a callback to process the result.
function ajaxCall(commandStr,callback){
var url=......//make a url with the command string
jquery.get(url,function(result){
//process the result using callback
callback(result);
});
}
The asynchronous call (ajaxCall) may take a while to be finished but I want it to do the same command after an interval (1000ms).
I want to write a function that is like this:
function ajaxCallRepeated(interval,commandStr,callback)
I tried closures like this:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(param1,param2,param3){
return (function(){
ajaxCall(param2,function(out,err){
if(param3)param3(out,err);
var functRef = callLater(param1,param2,param3);
setTimeout(functRef, interval);
});
});
}
//the first call
var functRef = callLater(interval,commandStr,callback);
setTimeout(functRef, interval);
}
Then I call it like this:
ajaxCallRepeated(2000,"ls",function(result){
alert(result);
});
But it only runs the command 2 times.
How can I write a function that will reschedule itself after it is called as a callback of an asynchronous function?
PS. I want to fire another Ajax call after the previous one is finished. Also, it worth to mention that axashCallRepeated() will be called with various parameters, so several Ajax calls are running in parallel, but for each commandStr, there is only one Ajax call going on, and after the Ajax call returns, another one will be fired after X seconds.
I would not use setTimeout to trigger the second Ajax call ! Because you never know how long it will take and if it's finished !
As far as you tagged your question right and you ARE using jquery you should consider something like this:
$.ajax({
type: 'POST',
url: url,
data: data,
success: function(){
// The AJAX is successfully done, now you trigger your custom event:
$(document).trigger('myAjaxHasCompleted');
},
dataType: dataType
});
$(function(){
//somehwere in your document ready block
$(document).on("myAjaxHasCompleted",function(){
$.ajax({
//execute the second one
});
});
});
So this would ensure that the ajax post is DONE and was successful and now you could execute the second one. I know its not the exact answer to your question but you should consider on using something like this ! Would make it safer I guess :-)
The key to solve this problem is to save a reference to the closure itself and use it when scheduling the next call:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(_interval,_commandString,_callback){
var closure=(function(){
ajaxCall(_commandString,function(out,err){
if(_callback)_callback(out,err);
setTimeout(closure,_interval);
});
});
return closure;
}
//now make a closure for every call to this function
var functRef = callLater(interval,commandString,callback);
//the first call
functRef();
}
It becomes easier to reason about if you separate things up a bit.
For example, the repetition logic doesn't have to know about AJAX or callbacks at all:
function mkRepeater(interval, fn, fnScope, fnArgs) {
var running;
function repeat() {
if (!running) return;
fn.apply(fnScope, fnArgs);
setTimeout(repeat, interval);
}
return {
start: function() { running = true; repeat(); },
stop: function() { running = false; }
};
}
You can use it like this:
var r = mkRepeater(2000, ajaxFunction, this, ["getStuff", callbackFn]);
r.start();
...
r.stop();

Categories

Resources