I want to make 2 api requests, but calling the second request inside the first's callback function doesn't seem right to me. Is there any way I can just call them both simultaneously and run the callback function only when I got respond from both?
You can use $.when() jquery function.
http://api.jquery.com/jquery.when/
You should use a variable outside the calls that increments on success. In the callback you test if it equals the number of calls, if true then you can call your finish method
Something like this:
var nbSuccess = 0;
var nbCalls = 2;
call_1.success
- nbSuccess++
- if nbSuccess == nbCalls {doFinish}
call_2.success
- nbSuccess++
- if nbSuccess == nbCalls {doFinish}
function doFinish...
Calling the second request inside the first one callback make them Synchronous. The second will only be called when the first has finished.
If you want two send the two call and handle only when the two are done you may (not tested)
Use the same callback for both and use a boolean to ensure both are done.
Something like (pseudo code):
var a1=false; a2=false;
a1.ajax(callback(){a1=true; doAction()}
a2.ajax(callback(){a2=true; doAction()}
function doAction() {
if (a1 && a2) {
...
}
You can do this
var response = 0;
var callback = function () {
if (response === 2){/* code */}
};
// first request
$.get(url).done(function () {
response++;
callback();
});
// second request
$.get(url).done(function () {
response++;
callback();
});
I hope this might helps you. Try this.
$.ajax("yourUrl",function(){
}).done(function(){
$.ajax("yourUrl",function(){
}).done(function(){
// Do your task
});
});
Related
I have a JavaScript function like the following.
function changeTheDom(var1, var2, var3) {
// Use DWR to get some server information
// In the DWR callback, add a element to DOM
}
This function is called in a couple of places in the page. Sometimes, in a loop. It's important that the elements be added to the DOM in the order that the changeTheDom function is called.
I originally tried adding DWREngine.setAsync(false); to the beginning of my function and DWREngine.setAsync(true); to the end of my function. While this worked, it was causing utter craziness on the rest of the page.
So I am wondering if there is a way to lock the changeTheDom function. I found this post but I couldn't really follow the else loop or how the lockingFunction was intended to be called.
Any help understanding that post or just making a locking procedure would be appreciated.
Don't try to lock anything. The cleanest way is always to adapt to the asynchronous nature of your code. So if you have an asynchronous function, use a callback. In your particular case I would suggest that you split your function up in one part that is executed before the asych call and one part that is executed afterwards:
function changeTheDomBefore(var1, var2, var3) {
//some code
//...
asyncFunction(function(result){
//this will be executed when the asynchronous function is done
changeTheDomAfter(var1, var2, var2, result);
});
}
function changeTheDomAfter(var1, var2, var3, asynchResult) {
//more code
//...
}
asyncFunction is the asynchronous function which, in this example, takes one argument - the callback function, which then calls your second changeTheDom function.
I think I finally got what you mean and I decided to create another answer, which is hopefully more helpful.
To preserve order when dealing with multiple asynchronous function calls, you could write a simple Queue class:
function Queue(){
var queue = [];
this.add = function(func, data) {
queue.push({func:func,data:data});
if (queue.length === 1) {
go();
}
};
function go() {
if (queue.length > 0) {
var func = queue[0].func,
data = queue[0].data;
//example of an async call with callback
async(function() {
func.apply(this, arguments);
queue.shift();
go();
});
}
}
};
var queue = new Queue();
function doit(data){
queue.add(function(result){
console.log(result);
}, data);
}
for (var i = 0; i < 10; i++) {
doit({
json: JSON.stringify({
index: i
}),
delay: 1 - i / 10.0
});
}
FIDDLE
So everytime you invoke your async function, you call queue.add() which adds your function in the queue and ensures that it will only execute when everything else in the queue is finished.
I am a complete noob to Ajax so please forgive me if this is a completely asinine piece of code:
for (var i=0; i<11; i++) {
jQuery('#position').html(i);
var offset = jQuery('#offset').html();
var postcall = 'controller.php?url='+encodeURIComponent(scrapurl)+'&scrape_absolute='+absoluteep+'&scrape_season='+season+'&scrape_show='+showslug+'&scrape_defimg='+encodeURIComponent(defaultimg)+'&offset='+offset;
jQuery.post(postcall,function(data){
jQuery('#offset').html(data);
});
}
The goal here is to execute controller.php with the given values and plug 'offset' back into each call using the returned info. It works but it runs from 0 to 10 instantly and my webserver rejects the subsequent calls.
My goal is to make sure it doesn't call the php again until the last operation has completed.
The key is to make your next AJAX call inside of your callback function. That way, your next post will not occur until the first finishes. In your code, because .post() is non-blocking (asynchronous), it continues the loop immediately, incrementing i/#position and firing off the next .post().
To solve this, encapsulate your .post() in a wrapper function. Have a counter that tracks how many times it has been called. Call the function from the callback of the .post(), and you end up with a recursive function that will do the calls in sequence:
var position=0;
function doNextAJAXPost() {
if(position < 11) {
jQuery('#position').html(position);
position++;
var offset = jQuery('#offset').html();
jQuery.post('controller.php?url='+encodeURIComponent(scrapurl)+'&scrape_absolute='+absoluteep+'&scrape_season='+season+'&scrape_show='+showslug+'&scrape_defimg='+encodeURIComponent(defaultimg)+'&offset='+offset, function(data){
jQuery('#offset').html(data);
doNextAJAXPost();
});
}
}
doNextAJAXPost();
use a self executing recursive function
(function callself(i) {
jQuery('#position').html(i);
var offset = jQuery('#offset').html();
var postcall = 'controller.php?url='+encodeURIComponent(scrapurl)+'&scrape_absolute='+absoluteep+'&scrape_season='+season+'&scrape_show='+showslug+'&scrape_defimg='+encodeURIComponent(defaultimg)+'&offset='+offset;
jQuery.post(postcall,function(data){
jQuery('#offset').html(data);
i++;
if ( i < 11 ) callself(i);
});
})(0)
I'm using a jQuery json function inside another function, how can I return an array made in the jQuery function as the return value of my parent function?
this is the basic setup
function getFlickrSet(flickr_photoset_id){
var images = [];
images = $.getJSON(url, function(data){ return data; // I HAVE THE DATA HERE };
return images // I HAVE NO DATA HERE
}
var myImages = getFlickrSet(23409823423423);
alert(myImages); // this gives me nothing
I have set up an example on jsfiddle right here, if you could tell me where my code is wrong, I would greatly appreciate it.
Thank you!
You can't. Instead, pass in a function:
function getFlickrSet(flickr_photoset_id, when_ready){
var images = [];
$.getJSON(url, function(data){
// prepare images
when_ready( images );
});
}
getFlickrSet(nnnn, function(images) {
alert(images);
});
Why can't you do that? Because the "$.getJSON()" call is asynchronous. By the time that the callback function is called (where you wrote, "I HAVE THE DATA HERE"), the outer function has returned already. You can't make the browser wait for that call to complete, so instead you design the API such that code can be passed in and run later when the result is available.
Well, Ajax is asynchronous (that's what the 'A' stands for), so you must do this in an asynchronous way, which boils down to callbacks. What you need to do is pass a callback function to your outer function that you want to be called ("called back," if you will) when the Ajax request completes. You could just give it 'alert' like this:
function getFlickrSet(flickr_photoset_id) {
images = $.getJSON(url, alert); // <-- just the name of the function, no ()
}
var myImages = getFlickrSet(23409823423423);
// => An alert pops up with the data!
...but more likely you'd write something like this:
function doSomethingWithData(data) { // we'll use this later
alert(data); // or whatever you want
}
function getFlickrSet(flickr_photoset_id, callback) {
// new parameter here for a function ------^
// to be given here -------v
images = $.getJSON(url, callback);
return images // I HAVE NO DATA HERE
}
var myImages = getFlickrSet(23409823423423, doSomethingWithData);
// => Your function `doSomethingWithData` will be called the data as a parameter
// when the $.getJSON request returns.
I want to delay a prepared method call (prepared= all parameters already set) for execution. Example:
I have a textfield with the following listener method:
var storedRequest = null;
function doAjaxRequest() {
//if there is no request at this moment, do the request
//otherwise: store the request and do nothing
}
//will be executed after a request is done
function callbackAjaxRequestComplete() {
//is storedRequest != null --> Execute that request (the last one)
}
So, is there a possiblity to store a PREPARED method call for execution?
var preparedMethod = method.bind(null, param1, param2, param3 /*, ... etc */);
Function.prototype.bind[docs]
You can do something like this:
var preparedOperation = function() {
return actualOperation(param1, param2, param3);
};
Then a call at any time to "preparedOperation" will be a call to your actual function.
The Functional.js library has some interesting support code for that sort of thing.
I have a Javascript object that requires 2 calls out to an external server to build its contents and do anything meaningful. The object is built such that instantiating an instance of it will automatically make these 2 calls. The 2 calls share a common callback function that operates on the returned data and then calls another method. The problem is that the next method should not be called until both methods return. Here is the code as I have implemented it currently:
foo.bar.Object = function() {
this.currentCallbacks = 0;
this.expectedCallbacks = 2;
this.function1 = function() {
// do stuff
var me = this;
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
};
this.function2 = function() {
// do stuff
var me = this;
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
};
this.commonCallback = function(resp) {
this.currentCallbacks++;
// do stuff
if (this.currentCallbacks == this.expectedCallbacks) {
// call new method
}
};
this.function1();
this.function2();
}
As you can see, I am forcing the object to continue after both calls have returned using a simple counter to validate they have both returned. This works but seems like a really poor implementation. I have only worked with Javascript for a few weeks now and am wondering if there is a better method for doing the same thing that I have yet to stumble upon.
Thanks for any and all help.
Unless you're willing to serialize the AJAX there is no other way that I can think of to do what you're proposing. That being said, I think what you have is fairly good, but you might want to clean up the structure a bit to not litter the object you're creating with initialization data.
Here is a function that might help you:
function gate(fn, number_of_calls_before_opening) {
return function() {
arguments.callee._call_count = (arguments.callee._call_count || 0) + 1;
if (arguments.callee._call_count >= number_of_calls_before_opening)
fn.apply(null, arguments);
};
}
This function is what's known as a higher-order function - a function that takes functions as arguments. This particular function returns a function that calls the passed function when it has been called number_of_calls_before_opening times. For example:
var f = gate(function(arg) { alert(arg); }, 2);
f('hello');
f('world'); // An alert will popup for this call.
You could make use of this as your callback method:
foo.bar = function() {
var callback = gate(this.method, 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
The second callback, whichever it is will ensure that method is called. But this leads to another problem: the gate function calls the passed function without any context, meaning this will refer to the global object, not the object that you are constructing. There are several ways to get around this: You can either close-over this by aliasing it to me or self. Or you can create another higher order function that does just that.
Here's what the first case would look like:
foo.bar = function() {
var me = this;
var callback = gate(function(a,b,c) { me.method(a,b,c); }, 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
In the latter case, the other higher order function would be something like the following:
function bind_context(context, fn) {
return function() {
return fn.apply(context, arguments);
};
}
This function returns a function that calls the passed function in the passed context. An example of it would be as follows:
var obj = {};
var func = function(name) { this.name = name; };
var method = bind_context(obj, func);
method('Your Name!');
alert(obj.name); // Your Name!
To put it in perspective, your code would look as follows:
foo.bar = function() {
var callback = gate(bind_context(this, this.method), 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
In any case, once you've made these refactorings you will have cleared up the object being constructed of all its members that are only needed for initialization.
I can add that Underscore.js has a nice little helper for this:
Creates a version of the function that will only be run after first
being called count times. Useful for grouping asynchronous responses,
where you want to be sure that all the async calls have finished,
before proceeding.
_.after(count, function)
The code for _after (as-of version 1.5.0):
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
The license info (as-of version 1.5.0)
There is barely another way than to have this counter. Another option would be to use an object {} and add a key for every request and remove it if finished. This way you would know immediately which has returned. But the solution stays the same.
You can change the code a little bit. If it is like in your example that you only need to call another function inside of commonCallback (I called it otherFunction) than you don't need the commonCallback. In order to save the context you did use closures already. Instead of
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
you could do it this way
foo.bar.sendRequest(new RequestObject, function(resp) {
--me.expectedCallbacks || me.otherFunction(resp);
});
That's some good stuff Mr. Kyle.
To put it a bit simpler, I usually use a Start and a Done function.
-The Start function takes a list of functions that will be executed.
-The Done function gets called by the callbacks of your functions that you passed to the start method.
-Additionally, you can pass a function, or list of functions to the done method that will be executed when the last callback completes.
The declarations look like this.
var PendingRequests = 0;
function Start(Requests) {
PendingRequests = Requests.length;
for (var i = 0; i < Requests.length; i++)
Requests[i]();
};
//Called when async responses complete.
function Done(CompletedEvents) {
PendingRequests--;
if (PendingRequests == 0) {
for (var i = 0; i < CompletedEvents.length; i++)
CompletedEvents[i]();
}
}
Here's a simple example using the google maps api.
//Variables
var originAddress = "*Some address/zip code here*"; //Location A
var formattedAddress; //Formatted address of Location B
var distance; //Distance between A and B
var location; //Location B
//This is the start function above. Passing an array of two functions defined below.
Start(new Array(GetPlaceDetails, GetDistances));
//This function makes a request to get detailed information on a place.
//Then callsback with the **GetPlaceDetailsComplete** function
function GetPlaceDetails() {
var request = {
reference: location.reference //Google maps reference id
};
var PlacesService = new google.maps.places.PlacesService(Map);
PlacesService.getDetails(request, GetPlaceDetailsComplete);
}
function GetPlaceDetailsComplete(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
formattedAddress = place.formatted_address;
Done(new Array(PrintDetails));
}
}
function GetDistances() {
distService = new google.maps.DistanceMatrixService();
distService.getDistanceMatrix(
{
origins: originAddress,
destinations: [location.geometry.location], //Location contains lat and lng
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.IMPERIAL,
avoidHighways: false,
avoidTolls: false
}, GetDistancesComplete);
}
function GetDistancesComplete(results, status) {
if (status == google.maps.DistanceMatrixStatus.OK) {
distance = results[0].distance.text;
Done(new Array(PrintDetails));
}
}
function PrintDetails() {
alert(*Whatever you feel like printing.*);
}
So in a nutshell, what we're doing here is
-Passing an array of functions to the Start function
-The Start function calls the functions in the array and sets the number of PendingRequests
-In the callbacks for our pending requests, we call the Done function
-The Done function takes an array of functions
-The Done function decrements the PendingRequests counter
-If their are no more pending requests, we call the functions passed to the Done function
That's a simple, but practicle example of sychronizing web calls. I tried to use an example of something that's widely used, so I went with the Google maps api. I hope someone finds this useful.
Another way would be to have a sync point thanks to a timer. It is not beautiful, but it has the advantage of not having to add the call to the next function inside the callback.
Here the function execute_jobs is the entry point. it take a list of data to execute simultaneously. It first sets the number of jobs to wait to the size of the list. Then it set a timer to test for the end condition (the number falling down to 0). And finally it sends a job for each data. Each job decrease the number of awaited jobs by one.
It would look like something like that:
var g_numJobs = 0;
function async_task(data) {
//
// ... execute the task on the data ...
//
// Decrease the number of jobs left to execute.
--g_numJobs;
}
function execute_jobs(list) {
// Set the number of jobs we want to wait for.
g_numJobs = list.length;
// Set the timer (test every 50ms).
var timer = setInterval(function() {
if(g_numJobs == 0) {
clearInterval(timer);
do_next_action();
}
}, 50);
// Send the jobs.
for(var i = 0; i < list.length; ++i) {
async_task(list[i]));
}
}
To improve this code you can do a Job and JobList classes. The Job would execute a callback and decrease the number of pending jobs, while the JobList would aggregate the timer and call the callback to the next action once the jobs are finished.
I shared the same frustration. As I chained more asynchronous calls, it became a callback hell. So, I came up with my own solution. I'm sure there are similar solutions out there, but I wanted to create something very simple and easy to use. Asynq is a script that I wrote to chain asynchronous tasks. So to run f2 after f1, you can do:
asynq.run(f1, f2)
You can chain as many functions as you want. You can also specify parameters or run a series of tasks on elements in an array too. I hope this library can solve your issues or similar issues others are having.