Ajax loop and response order - javascript

I am retrieving a series of data from a server (geoserver) using $.ajax.
The (simplified) request looks like this:
var dataList=[];
//var urllist= // a list of several URLs to request data from
$.each(urllist,function(i) {
$.ajax({
jsonpCallback: 'getJson',
type: 'GET',
url: urllist[i],
dataType: 'jsonp',
success: function(data) {
dataList[i]=data.value;
}
})
});
I need to write to the global variable dataList because I need to fire an event after all requests from urllist are finished. (I've got deferreds implemented like so).
The problem is that the finished list is always in a different order. I need the result to be in the same order as the requests.
It might be a problem of closure where the index i that is passed on to the ajax function and the allocation to dataList that is happening at a later point (when the each loop has moved on).
I tried taking care of that like this but the problem remains the same. Also $.each like in the code above should create a seperate closure for every iteration anyway.
I've managed to implement a recursive function but its synchronous.
edit: suggested duplicate does not deal with looped ajax requests

You can access all the results in $.when callback in the correct order
// map an array of promises
var deferreds = urllist.map(function(url){
// return the promise that `$.ajax` returns
return $.ajax({
url: url,
dataType: 'jsonp'
}).then(function(data){
return data.value;
})
});
$.when.apply($, deferreds).then(function(results){
// results will be array of each `data.value` in proper order
var datalist = results;
// now do whatever you were doing with original datalist
$.each(datalist....
}).fail(function(){
// Probably want to catch failure
}).always(function(){
// Or use always if you want to do the same thing
// whether the call succeeds or fails
});

The problem was not related to the deferreds but to the jsonp or the related jsonpcallback required for the request. Requesting the data as json solved the problem
credits to #charlietfl for the answer over at: Looped ajax request. Error handling and return order
For anyone looking this up: You most likely have to to enable Cross-Origin Resource Sharing on geoserver to be able to access the JSON directly

Related

Aborting / canceling running AJAX calls before execute new AJAX in JS

I've never done this type of manipulation of AJAX calls (to stop/abort/cancel or ignore? already running AJAX calls before the execution of a new one) before so I really don't understand how to do that and would appreciate some direction.
I have a page in my app where I make a number of AJAX calls to fill dynamically the data in my table (Object Name, Object Fit, Object Progress) when the page loads. For example, there are 5 rows in the table. So I call
$.post("/getFit", {objectId: objectId}, function (result) { manipulation with result }
and
$.post("/getProgress", {objectId: objectId}, function (result) { manipulation with result }
5 times each in the loop -- one for each of the objects.
The first column of my table has links to more detail on the object, and clicking on them I call another AJAX:
$(document).off('click', '.js_object').on('click', '.js_object', function (e) {
var objectId = $(this).attr("id")
$.post("/viewObject", {objectId: objectId}, function (result) {document.getElementById("main_window_content").innerHTML = result; });
})
The problem is that the browser will not render the results of the last AJAX call (/viewObject) until it has received the results of all of the previous calls (/getFit x5 and /getProgress x5).
As a result, a user that wants to drill into the detail on an object needs to wait until the AJAX calls for the other objects are complete before they see anything.
So I struggle with how to stop/abort/cancel (or ignore?) "/getProgress" and "/getFit" so we can fully execute "/viewObject" and view the results of it.
I would very much appreciate your help.
Use xhr.abort() to kill the xhr requests as shown in the below code in JS. I believe there is ajax.abort(); in JQuery
var xhr = $.ajax({
type: "POST",
url: "XXX.php",
data: "name=marry&location=London",
success: function(msg){
alert( "The Data Saved: " + msg );
}
});
//kill the request
xhr.abort()
If you want execute one ajax after another, and you need all requests to work to show the final result, you can use .done():
$.ajax({
url:'/getFit',
data:{objectId:objectId}
})
.done(function(data){
//do something with the results then call /getProgress
$.ajax({
url:'/getProgress',
data:{objectId:objectId}
})
.done(function(data){
//do something with the results then call /viewObject
$.post("/viewObject"....
})
});
That way you only show /viewObject if the others calls were successfull

Asynchronous Lazy Loading

currently, we are using the SAP HANA Database. To get data, we will use a Node.JS-API, which we will call via AJAX, to get the advantages by async. So here is the problem:
We have many pages where we need the same data (e.g. customer data). To do so, I wanted to create a Library, which does the actual data calls, so that i just need to call db.getCustomer([ID]). In order to get a return value from AJAX, I have to set async: false within the AJAX call.
My question is now, is it possible to create a data-call-library asynchronously? Is it a good practice to encapsulate the databinding (using so called DAO)?
I'm a bit confused, because another dev told me to just use the same AJAX-call over and over again, to not loose the async and it is a better practice anyway.
Here is my actual AJAX-call as an example:
getCustomer: function( CID ) {
var aUrl = 'http://example.com/api/customer/' + CID,
returnData
;
jQuery.ajax({
url: aUrl,
method: 'GET',
dataType: 'json',
contentType: "application/json",
async: false,
success: function(data) {
returnData = data;
}
});
return returnData;
},
// other ajax calls
// to get the data via 1-liner
thank you for clarification!
Actually, it makes no sense to return data from a callback in a synchronous function; normally, you would store the returned callback data into your model, so your view/controller gets automatically updated.
If you really need your method to return async data, have a look at Deferred or Promises

Use data from ajax call without using async: false

I'm pretty new to JS/ajax and was a bit confused on ajax data/success returns.
I have the following ajax code. I want it to run the code in succes: after the ajax call has received the json from the imgur api.
It is only working for me with async: false. I thought the bit in success: would only run after the data has been retrieved, but for some reason data is null if aysnc is true.
I've read a lot of SO answers/jQuery documentation but I can't seem to figure out how to get it to not hang the browser with async: false
This code might be running 100+ times on a page (i.e. reddit.com/r/pics with 100 links loaded on one page), so async: false is not only bad practice but also renders the page unusable for 5 to 6 seconds, which is unacceptable.
I've tried making ajax a var then calling that var .success(function(data) ...); but it still does not work. I've also tried replacing success with complete.
It all only ever works with async: false
Any suggestions would be greatly appreciated!
Thanks!
$.ajax({
type: "GET",
url: "https://api.imgur.com/3/gallery/" + hash,
dataType: "json",
headers:{
'Authorization':'Client-ID c606aeeec9ca098'
},
async: false,
success: function(data) {
if(data.data.is_album == true) {
if(data.data.images_count == 1){
el[j].href = el[j].href.replace(/(http(s)?:\/\/)?(www\.)?(m\.)?imgur.com\/gallery\/.*/, data.data.images[0].link);
}else{
el[j].href = el[j].href.replace(/(http(s)?:\/\/)?(www\.)?(m\.)?imgur.com\/gallery\//, "https://imgur.com/a/");
}
}
else{
el[j].href = el[j].href.replace(/(.*)?(http(s)?:\/\/)?(www\.)?(m\.)?imgur.com\/.*/, data.data.link);
}
}
});
You might find the jQuery Deferred object (Promises interface) to be helpful. It allows you to do stuff like:
var promise = $.ajax("/myServerScript1");
function getStuff() {
return $.ajax("/myServerScript2");
}
promise.then(getStuff).then(function(myServerScript2Data){
// Do something with myServerScript2Data
});
Resource (this is well written and a necessary read for anyone using AJAX):
http://www.htmlgoodies.com/beyond/javascript/making-promises-with-jquery-deferred.html
I can't see what doesn't work on your code but I can tell you what is not recommended.
Always have a failure handler : You have to put a function in the fail field of your request object. If you don't, you will not be able to see any errors.
Keep the test simple : You should begin by only displaying the value of the request result instead making some calculation with it. This way you can debug ONLY the request call and you are sure that the problem doesn't came from the calculation code
Use Promises API : As previously pointed by the #gibberish, the modern way to do what you want is to use Promises. You can use jQuery's promises or any other like ES6 Promises (that one requires a compilation time for most of browsers)

Javascript Synchronization with JSON Requests

How can I make sure that a piece of code has executed completely before executing another? I am sending some ajax requests to a server and then using the returned data to generate the rest of the webpage. the things is, is that i need to have all that data in the webpage to proceed with the rest of the code as that code will affect what has been generated and, that code, runs before the json requests and all of that have finished... is there any way I can make sure this does not happen? I managed to solve it by performing the requests and then asking the user to press a button but that is a total no-sense way of doing it.
Any ideas?
Here is some code: The problem is that the second line is executed before the first (there are many calls to similar JSON functions).
$.getJSON(url, function(data){ $("#mycontent").append("..... stuff here...... create loads of dibs with class set to mydivclass"); });
...
$("div.mydivclass").hide();
Unforunately I cannot use the ajax synchronous property because: "dataType: "jsonp" requests do not support synchronous operations"
If you are using jQuery 1.5+ you can make use of deferreds to solve your issue:
function first_ajax_request() {
return jQuery.ajax(
// Your settings here
success: success_function_1
);
}
function second_ajax_request() {
return jQuery.ajax(
// Your settings here
success: success_function_2
);
}
function final_sucess_callback() {
// Do all your display work.
}
jQuery.when(first_ajax_request(),
second_ajax_request()).then(final_success_callback);
There is an excellent article on the topic that you should read up on as well by Eric Hynds. He gives some examples of exactly the kind of problem you are trying to solve.
jquery requests are asynchonize by default , so your code does not wait for the response , so you have no guarantee that code after request will execute after the response , so you can set the request synchronize by set the async property false , now the request is synchronize and you can gurantee the rest of the code will execute after the response from the server ,
like this .
$.ajax({
url: "page.php",
processData: false,
data: xmlDocument,,
async:false,
success: handleResponse
});

Iterate on array, calling $.ajax for each element. Return array with all ajax results

I've just started working with JavaScript for non-trivial things, so this is probably straightforward...
What I'm trying to accomplish: iterate on an array of product references, fetch the JSON for each reference, and return an array of all the product information (with a hash-like structure indexed by reference).
What I've tried:
function fetchProductData(references){
var product_data = new Object();
references.forEach(function(ref){
$.ajax({
url: "http://localhost:3000/products/find.js?reference=" + ref,
dataType: "jsonp",
type: "GET",
processData: false,
contentType: "application/json",
success: function(data) {
product_data[ref] = data;
}
});
});
alert('before return: ' + product_data);
return product_data;
};
$(document).ready(function(){
var products = fetchProductData(references);
alert('products : ' + products);
});
Here's what I don't understand: the first time I call alert to display the array contents, the array is empty. However, on the second call, the array is populated with the data I want.
In other words, the "products :" alert displays the data I want in the code above. But if I comment the "before return: " alert, it no longer does. Why is this?
So my question is: how can I have jQuery make several $.ajax call to fetch product information, collect that information in an array, and return that array so I can use it elsewhere in my code?
Also, why is the data in the variable magically accessible after it is referenced in an alert?
The "A" in "AJAX" stands for "asynchronous" :). Your program doesn't wait for the call to complete before going on to the next iteration, meaning you'll probably not get all of your data. Also the alert has the same problem. Operation to concat 'before return:' to the string add just enough time to get some data in the variable. On a faster machine you might find you never get data.
I think you really need to rethink your approach. It's not a good idea to have multiple AJAX requests in a loop. It will greatly increase latency of the page. Pass all your parameters once using JSON, then have your server side script loop through that and return a single response in JSON.
function fetchProductData(references){
// make sure your "references" is a JSON object
$.getJSON('http://server/side/url', {'json':references}, function(product_data) {
// do something with product_data (alert them, put them in an array, etc)
});
}
function fetchProductData(references, cb){
var length = 0;
var product_data = new Object();
references.forEach(function(ref){
length++;
$.ajax({
url: "http://localhost:3000/products/find.js?reference=" + ref,
dataType: "jsonp",
type: "GET",
processData: false,
contentType: "application/json",
success: function(data) {
product_data[ref] = data;
if (++count === length) {
cb(product_data);
}
}
});
});
};
$(document).ready(function(){
var products = fetchProductData(references, function(products) {
alert('products : ' + products);
});
});
Use a callback on your asynchronous operation.
The reason it appears to work with the alert call is because alerting a message gives ajax enough time to populate your array. The return statement is only triggered after you click OK on the alert box giving your code a window of 250ms to populate the array with data.
You are executing you ajax query in async mode. And you want a sync result. Try to add:
async: false
Hope this helps.
your $.ajax call is asynchronous, so what is happening is that the first time you make the call, your javascript makes the call, moves on to the next line (alert) and then loops. You're data hasn't returned at that point yet. What you can do to remedy this is to set the async: false option in your $.ajax call.
This is an asynchronous operation. The only sure way to know when the data is ready is in the callback function: success: function () {...}, which gets called when the data has finally returned. Put your alert in there.

Categories