multiple ajax async not in order and need synchronous behavior - javascript

Sorry, My first language is not English. I am not sure that if I explain my question properly.
My code is like a main function have two ajax functions (Use ajax function to get foursquare API)
main(){
ajax1();
ajax2();
all other codes
}
the ajax2() function has to get result from ajax1() as input and then return result(actually result was pushed in to global array).
all other codes should be processed after two ajax functions are finished. I tried the asyn: false but it is not working. My html file include newest jquery like this
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js" ></script>
I try the jquery function $.when().done() function and the first ajax works. However, the second ajax() function was in the for loop. The for loop will destroy the mechanism of $.when().done() function:
first ajax: in firstjson function
Second ajax: in transfer function
function firstjson(tmpName,tmpLoc,PhotoJson,foursq){
return $.ajax({
type: 'GET',
url: foursq,
dataType: 'jsonp',
success: function(json) {
for (i = 0; i < 3; i++) {
var resultname = json['response']['venues'][i].name;
var resultlocation = json['response']['venues'][i].location;
var resultlat = resultlocation.lat;
var resultlng = resultlocation.lng;
var tmpmarker = new google.maps.LatLng(resultlat,resultlng)
tmpName.push(resultname);
tmpLoc.push(tmpmarker);
var resultid = json['response']['venues'][i].id;
var tmpPhotoJason = 'https://api.foursquare.com/v2/venues/'+ resultid +'/photos?';
PhotoJson.push(tmpPhotoJason);
}
}
});
}
function transfer(PhotoJson,PhotoURL){
for (i = 0; i < 3; i++) {
return $.ajax({
type: 'GET',
url: PhotoJson[i],
dataType: 'jsonp',
success: function(json) {
resultphoto = json['response']['photos']['items'];
photoprefix = resultphoto[i].prefix;
photopresuffix = resultphoto[i].suffix;
photourl = photoprefix+"150x150" + photopresuffix;
PhotoURL.push(photourl);
}
});
}
}
$.when(firstjson(tmpName,tmpLoc,PhotoJson,foursq)).done(function(){
alert("test1");
$.when(transfer(PhotoJson,PhotoURL).done(function(){
console.log(PhotoURL);
all other codes!!!!
});
});
//PhotoURL is global array
So the first "when" function work properly. alert("test1") work after the firstjson was done. However the for loop inside transfer function will break the when function. How can I fix the problem. Please help me. I will appreciate you can give me any related information. Thanks!!!

This will execute ajax2 after ajax1
function anotherMethod(){
//Here you do all that you want to do after the last $ajax call
}
main(){
firstjson(tmpName,tmpLoc,PhotoJson,foursq)
.then(transfer(PhotoJson,PhotoURL))
.then(anotherMethod);
}
As you are returning a promise from the first with the "return $ajax..."
So you organice your code like this:
in methods with ajax calls you return the call as you are doing now
return $.ajax();
that returns a promise that you chain.
And you put what you want to do in another method so you call it in the last "then".

Non-Blocking Example
You should use non-blocking code. You can turn async off (async: false) but this can easily be done in a non-blocking manor using callback functions.
function main(){
$.ajax({ // First ajax call (ajax1)
url: "first/ajax/url",
type: "GET", // POST or GET
data: {}, // POST or GET data being passed to first URL
success: function(x){ // Callback when request is successfully returned
// x is your returned data
$.ajax({ // Second ajax call (ajax2)
url: "second/ajax/url",
type: "GET", // POST or GET
data: {
thatThing: x
}, // POST or GET data passed to second URL
success: function(y){
// y is your second returned data
// all other codes that use y should be here
}
});
}
})
}
This would be the non-blocking approach, nest your function within "success" callback functions. Nest ajax2 within ajax1's "success" callback to ensure that ajax2 is not executed before ajax1 has returned and nest your "all other codes" inside the "success" callback of ajax2 to ensure they are not executed until ajax2 has returned.
Blocking Example
If you absolutely must (please avoid at all cost) you can disable async which will block all JavaScript code from executing until the ajax has returned. This may cause your browser to temporarily freeze until the ajax request has returned (depending on the browser).
function main(){
var x = ajax1();
var y = ajax2(x);
window["y"] = y; // push to global as you requested but why?
// All other codes that can now use y
}
function ajax1(){
var x;
$.ajax({
url: "first/ajax/url",
async: false,
type: "GET", // POST or GET,
data: {}, // POST or GET data being passed to first URL
success: function(r){x=r}
});
return x;
}
function ajax2(x){
var y;
$.ajax({
url: "second/ajax/url",
async: false,
type: "GET", // POST or GET,
data: {
thatThing: x
}, // POST or GET data being passed to second URL
success: function(r){y=r}
});
return y;
}
Once again I stress, try not to disable async that will cause your code to block and is BAD code. If you absolutely 100% have to for some reason than than it can be done but you should attempt to learn how to write non-blocking code using callbacks as the first example does.
Social Network Example
Now I'll do an example of an ajax call to get an array of your friends IDs, and then a series of ajax calls to get each of your friends profiles. The first ajax will get the list, the second will get their profiles and store then, and then when all profiles have been retrieved some other code can be ran.
For this example, the url https://api.site.com/{userID}/friends/ retrieves an Object with a list of friends IDs for a particular user, and https://api.site.com/{userID}/profile/ gets any users profile.
Obviously this is a simplified api as you will probably need to first establish a connection with a apikey and get a token for this connection and the token would likely need to be passed to the api uris but I think it should still illustrate the point.
function getFriends(userID, callback){
$.ajax({
url: "https://api.site.com/"+userID+"/friends/",
success: function(x){
var counter = 0;
var profiles = [];
for(var i=0;i<x.friendIDs.length;i++){
$.ajax({
url: "https://api.site.com/"+x.friendIDs[i]+"/profile/",
success: function(profile){
profiles.push(profile);
counter++;
if(counter == x.friendIDs.length) callback(profiles);
}
});
}
}
});
}
getFreinds("dustinpoissant", function(friends){
// Do stuff with the 'friends' array here
})
This example is "Non-blocking", if this example were done in a "blocking" way then we would ask for 1 friends profile, then wait for its response, then request the next and wait and so on. If we had hundreds of friends you can see how this would take a very long time for all ajax calls to complete. In this example, which is non-blocking, all requests for profiles are made at the same time (within 1ms) and then can all be returned at almost exactly the same time and a counter is used to see if we have gotten responses from all the requests. This is way way way faster than using the blocking method especially if you have lots of friends.

Related

Why is my Ajax callback being processed too soon?

I have a general ajax function which I'm calling from loads of places in my code. It's pretty standard except for some extra debugging stuff I've recently added (to try to solve this issue), with a global 'ajaxworking' variable:
rideData.myAjax = function (url, type, data, successfunc) {
var dataJson = JSON.stringify(data),
thisurl = quilkinUrlBase() + url;
if (ajaxworking.length > 0) {
console.log(thisurl + ": concurrent Ajax call with: " + ajaxworking);
}
ajaxworking = thisurl;
$.ajax({
type: type,
data: dataJson,
url: thisurl,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: true,
success: function (response) {
ajaxworking = '';
successfunc(response);
},
error: webRequestFailed
});
};
Now, there's one section of my code where a second ajax call is made depending on the result of the first:
getWebRides = function (date) {
var rideIDs = [];
var intdays = bleTime.toIntDays(date);
rideData.myAjax("GetRidesForDate", "POST", intdays, function (response) {
rides = response;
if (rides.length === 0) {
$('#ridelist').empty(); // this will also remove any handlers
qPopup.Alert("No rides found for " + bleTime.DateString(date));
return null;
}
$.each(rides, function (index) {
rideIDs.push(rides[index].rideID);
});
GetParticipants(rideIDs);
});
},
'GetParticipants' (which also calls 'myAjax') works fine - most of the time. But in another part of my code, 'GetWebRides' is itself called directly after another ajax call - i.e. there are 3 calls, each successive one depending on the previous. The 'top-level' call is as follows:
rideData.myAjax("SaveRide", "POST", ride, function (response) {
// if successful, response should be just a new ID
if (response.length < 5) {
// document re-arrangement code snipped here for brevity
getWebRides(date);
}
else {
qPopup.Alert(response);
}
});
so, only when there are three successive calls like this, I'm getting the 'concurrent' catch in the third one:
GetParticipants: concurrent call with GetRidesForDate
and (if allowed to proceed) this causes a nasty probem at the server with datareaders already being open. But why is this only occurring when GetParticipants is called as the third in the chain?
I see, after some research. that there are now other ways of arranging async calls, e.g. using 'Promises', but I'd like to understand what's going on here.
Solved this.
Part of the 'document re-arrangement code' that I had commented out for this post, was in fact calling another Ajax call indirectly (very indirectly, hence it took a long time to find).

I can't seem to break out of A $.each() loop

I can't seem to manage to break out of my each loop if the ajax returns an error. I've tried
return false;
and other similar thing but the $.each still continues to run.
I need to be able to do this so that I can display error messages from my back end after posting it via ajax(I know this is bad practice however a client needed to be able to be able to send multiple forms off at once.).
Can anyone explain what I've done wrong?
var postAll = function(button_clicked)
{
e.preventDefault();
var form_response = [];
var formsCollection = document.getElementsByTagName("form");
$.each(formsCollection, function (key, value)
{
console.log(value.action);
console.log(value.id);
var url = value.action;
var id = value.id;
var data = ($('#' + id + '').serialize());
if (id == 'additionalInfo')
{
data = {'Add_info': $('#Add_info').val(),};
}
if (id != 'DONE')
{
$.ajax({
type: "POST",
dataType: 'json',
url: url,
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-TOKEN',$("#token").attr('content'));
},
data: data,
success: function (data)
{
console.log('success'); // show response from the php script.
form_response.push(data); // show response from the php script.
},
error: function (data)
{
console.log('fail'); // show response from the php script.
display_errors(data, id); // show response from the php script.
return true;
}
});
}
});
}
AJAX is asynchronous, when executing your $.each function it will execute the AJAX call and "Not wait" for the others to finish
To solve your problem you'll have to write a function that will execute the first ajax call and in the success it will execute itself again with the second ajax call.
Example:
var data = [form1,form2...etc];
function letsLoop(data,index = 0){
$.ajax({
url:....
success: function(){
letsLoop(data,index+1);
},
error: function(){
}
});
}
and here you call your function:
letsLoop(data,0);
If by breaking out of the loop you mean the return in your error handler, then it won't work as you think it would.
Your loop creates asynchronous requests 'at once'. Then each of these requests is handled by the browser (more or less simultaneously), then come responses. So by the time your error handler runs the loop has long finished.
BTW, the return in your case relates to the error handler, not the function inside the loop.
So, to achieve what you want you should 'queue' your AJAX requests and perform them one by one.
One possible solution is to create an array of forms then take (and remove it from the array) the first one, perform a request, on a response repeat the whole thing, and keep repeating until the array is empty.

how to use properties and methods withing a JavaScript class to exchange data?

i have small issue with exchanging data in between methods in a JavaScript object (class):
var TEST = (function () {
var TEST = function() {
};
TEST.prototype.get = function() {
$.ajax({
type: "GET",
url: "http://test.com/getall",
dataType: "json",
success: function (data) {
return data; // if i console log this i will get a json obj
}
});
};
TEST.prototype.parse = function(data) {
$.each(this.get(), function(k, v){
console.log(v);
});
};
return TEST;
})();
so i am trying to call one method in the each statement in another method. the issue is that
the response is undefined.
i also tried it like this, but with he same result
var testing = new TEST();
var get = testing.get();
testing.parse(get);
What am i missing? how can i return the data from this.get to be used in this.parse.
thanks
$.ajax() per default is asynchronous. That means, that the execution of your function get() wont wait until the request is finished. Hence you return no value from it, which results in undefined being returned.
In order to have your get() function be able to return a value, you would have to do the request in a synchronous way and set a variable in the outer function (as success itself is just another function, whose return value is not caught):
TEST.prototype.get = function() {
var result;
$.ajax({
type: "GET",
url: "http://test.com/getall",
async: false, // this is the important part!
dataType: "json",
success: function (data) {
result = data;
}
});
return result;
};
EDIT
As mentioned by #pebbl, this will halt the execution of all your scripts, until the request is done. Hence your whole page will be blocked for the time being.
The general approach is to use callbacks in such cases, which will be executed once the requests finished. So in your case something like this:
TEST.prototype.get = function( cb ) {
$.ajax({
type: "GET",
url: "http://test.com/getall",
dataType: "json",
success: function (data) {
cb( data );
}
});
};
with later on calling like this:
var testing = new TEST();
testing.get( function( data ) {
testing.parse( data );
});
You can't construct your function this way as you are relying on an asyncronous call, which will return it's result outside of the normal execution flow. The only way you can actually receive the result of your .get function is to use a callback.
Put simply your value isn't being returned from the .get function, it's being returned from the callback you are passing into jQuery's .ajax method.
You'd be far better off redesigning your code so as to still support the asyncronous call -- rather than disabling async.
A rough idea is to change your parse function like so:
TEST.prototype.parse = function(data) {
this.get(function(result){
$.each(result, function(k, v){
console.log(v);
});
});
};
And to change your get function accordingly:
TEST.prototype.get = function(callback) {
$.ajax({
type: "GET",
url: "http://test.com/getall",
dataType: "json",
success: callback
});
};
The above is just a quick example, you'd be wise reading up on the following jQuery topics:
http://api.jquery.com/promise/
http://api.jquery.com/category/deferred-object/
If you design your code around the promise pattern you'll find it complicated at first, but it gives you a lot of power in your code -- and gets around the whole callback stacking madness you can end up with when dealing in ajax calls.
Whilst it's not entirely clear from the jQuery.ajax documentation, this function returns a jqXHR object which implements the promise interface. So this means you can use the promise methods done, always and fail.
http://api.jquery.com/jQuery.ajax/

Looping through array with callback

I am trying to run through a array send to a php file and on a callback send the next value after the php has completed its download. Here what i have so far.
my array come through as follows.
["http://example.com/test1.zip", "http://example.com/test2.zip", "http://example.com/test3.zip", "http://example.com/test4.zip", "http://example.com/test5.zip"]
above is the output from console.log(values); below. it grabs some urls from checkbox values.
$('.geturls').live('click',function(){
var values = new Array();
$.each($("input[name='downloadQue[]']:checked"), function() {
values.push($(this).val());
ajaxRequest($(this).val(),function(response){
console.log(response);
});
});
console.log(values);
return false;
});
this then calls a ajax function which i am trying to do a callback on.
function ajaxRequest(urlSend,callback){
var send = {
url: urlSend
}
$.ajax({
type: "POST",
url: "<?php echo base_url(); ?>index.php/upload",
data: send,
//dataType: "json",
//timeout: 8000,
beforeSend: function() {
},
success: function(response) {
callback('added');
},
error: function (response) {
callback('false');
}
});
}
this will then send to a php file.
function upload(){
$output = shell_exec("wget {$_POST['url']} 2>&1");
return true;
}
What i am trying to do is after the callback from one url which it has download fully then grab the next value from the array and download that url and so on until all the urls in the array are downloaded fully.
at the moment it just downloads the first value and then crashes because it doesn't restart the loop after a return value of true is returned.
Hope this makes sense to someone just looking for some help on the best way to loop through an array of values with a callback after complete.
May be this structure can help you. In this variant you go next URL only after successful completion of the previous Ajax call.
var arr = ['url0','url1','url2','url3'];
var index = 0;
function Run(){
DoAjax(arr[index]);
}
function Next( ){
if(arr.count = index-1)
{
index =0;
return;
}else{
DoAjax(arr[index ]);
}
}
function DoAjax(url){
$.ajax({
type: "POST",
url: url,
data: send,
beforeSend: function() {
},
success: function(response) {
index ++;
Next();
// Addition logic if needed
},
error: function (response) {
}
});
}
Run()
Now that I have a bit more time, I thought it would be good to show an alternative which takes advantage of the fact that jquery ajax is now implemented as a deferred. Meaning you can use pipe chaining to do all the work for you. I've also eliminated the callbacks by taking advantage of the deferred behavior.
This should give you the idea.
// Use jquery deferred pipe chaining to force
// async functions to run sequentially
var dfd = $.Deferred(),
dfdNext = dfd,
x,
values = [],
// The important thing to understand here is that
// you are returning the value of $.ajax to the caller.
// The caller will then get the promise from the deferred.
ajaxRequest = function (urlSend) {
var send = {
url: urlSend
}
return $.ajax({
type: "POST",
url: "<?php echo base_url(); ?>index.php/upload",
data: send,
});
};
// Starts things running. You should be able to put this anywhere
// in the script, including at the end and the code will work the same.
dfd.resolve();
// Deferred pipe chaining. This is the main part of the logic.
// What you want to note here is that a new ajax call will
// not start until the previous
// ajax call is completely finished.
// Also note that we've moved the code that would
// normally be in the callback.
// Finally notice how we are chaining the pipes by
// replacing dfdNext with the return value from the
// current pipe.
for (x = 1; x <= 4; x++) {
values.push(x);
dfdNext = dfdNext.pipe(function () {
var value = values.shift();
return requestAjax(value).
done(function(response) {
// Code here that you would have
// put in your callback.
console.log(response);
}).
fail(function(response) {
console.log(response);
};
});
}
Working example you can play with on jsFiddle.

Javascript value is returned from webservice but will not show unless a breakpoint is used

I have a javascript function that calls a web service. The data comeback (I see the Jason return in FireBug) the value is blank when I attempt to use it unless I set a break point. With a break point set the value can be used, without it is not available.
Here is a snippet of the offending call.
function getTheNote(noteCode){
var _myNote = "";
var theID = $('#CustNo').val();
var myDTO = { 'theID': theID, 'noteCode': noteCode, };
var toPass = JSON.stringify(myDTO);
$.ajax({
type: 'POST',
contentType: "application/json; charset=utf-8",
dataType: "json",
url: "AR_Cust_Mgt.aspx/getNote",
data: toPass,
success: function (data) {
_myNote = data.d;
}
});
//setTimeout(_myNote += _myNote, 120000);
//for(var x = 0; x < 200000; x++){}
//return _myNote;
alert(_myNote);
}
Originally I was sending the value back to a calling function the return statement is where I would set my break point and the data would be returned, without nothing. Now you can see I attempted to use an alert inside the function with the same results.
With a break point I get a value without I get nothing, I have even attempted to use some delays.
Please help.
The ajax call is asynchronous. Anything you want to do with the result needs to be in your anonymous function success: function(data) { ... or the anonymous function needs to call other functions to do stuff.
As it is coded now, $.ajax will be called, the script execution continues on before the ajax call returns.
small change, big difference: you are not calling alert IN the succes function
success: function (data) {
_myNote = data.d;
alert(_myNote);
}

Categories

Resources