I have an array that can hold an unknown amount of indexes in it. Each index is used to send data with an ajax call. I am looping through with a for loop gathering the data from the successful call and pushing it into an empty array. At the end of the unknown amount of calls I then need to use that newly gathered array in my view. newDataArray is executed at the bottom before the loops are done and therefor it is still empty. How do I finish all the calls then do what is at the bottom?
If it helps, I am doing this in React with the Flux pattern. But the same issue may be done not in React. Here is a mock sample of what I am trying to do:
JS
case 'execute-calls':
//This is the new array to push to
var newDataArray = [];
//Url to call
var url = 'http://dev.markitondemand.com/Api/v2/Quote/jsonp';
for(let i = 0; i < payload.data.length; i++){
//given array of data that needs to be sent with call
let symb = { symbol: payload.data[i]};
$.ajax({
data: symb,
url: url,
dataType: "jsonp",
})
.done(function(data){
let updatedData = {
//...data that is stored from response
};
newDataArray.push(updatedData);
})
.fail(function(error){
//console.log(error);
});
}
//This will be updating the state object which is above the switch cases
//However this is ran before the end of the loops so newDataArray is empty
var updateTicker = {
updatedTicker: true,
updatedTickerSymbols: newDataArray
};
assign(stockData,updateTicker);
getStockData.emitChange();
break;
You can make use of the fact that $.ajax() actually returns a deferred object, and use it to create an array of deferreds. e.g.
var symbols = [1, 2, 3, 4];
var deferreds = symbols.map(function (symbol) {
return $.ajax({
url: 'http://dev.markitondemand.com/MODApis/Api/v2/Quote/jsonp',
data: { symbol: symbol },
dataType: 'jsonp'
});
});
You can resolve multiple deferreds at once with $.when(). There is a complication however, $.when() expects a list of parameters rather than array. We can solve this by using Function#apply.
To add to the complication, the callback function is also called with a list of arguments. Since we don't know how many arguments there are, we'll use the arguments pseudo-array. And since arguments isn't an actual array, we'll loop through it by using Function#call on Array#prototype.
$.when.apply($, deferreds).done(function () {
Array.prototype.forEach.call(arguments, function (response) {
console.log(response[0].Message);
});
}).fail(function (jqXHR, textStatus, error) {
console.error(error);
});
[UPDATED to include fail() call]
If you're using ES6 this is much more elegant:
$.when(...deferreds).done((...responses) => {
responses.forEach((response) => {
console.log(response[0].Message);
});
});
When ever you are dealing with ajax calls and have to do some operations at the end of all async calls then better choice would be to use Callback functions.
Modifying your code to use the call back,
function AsyncLoopHandler(index) {
if (index > payload.data.length) {
// all the indexes have finished ajax calls do your next step here
var updateTicker = {
updatedTicker: true,
updatedTickerSymbols: newDataArray
};
assign(stockData, updateTicker);
getStockData.emitChange();
}
else {
//given array of data that needs to be sent with call
let symb = { symbol: payload.data[index] };
$.ajax({
data: symb,
url: url,
dataType: "jsonp",
})
.done(function (data) {
let updatedData = {
//...data that is stored from response
};
newDataArray.push(updatedData);
AsyncLoopHandler(index++); // call the function again with new index
})
.fail(function (error) {
//console.log(error);
});
}
}
Now for starting this recursive function just start it by passing the index 0.
AsyncLoopHandler(0);
So all the ajax calls will be executed one after the other as if its an synchronous requests, And the if check will see if all the indexes are complete and then run your logic. Let me know if this helps
suggest use promise, logic would like
var urls= [x,x,x,x];
var results = [];
var qs = $.map(urls,function(url){
return function(){
var deferred = Q.defer();
$.ajax({
success:function(){
results.push(url)
deferred.reslove();
},error:function(){
deferred.reslove();
}
})
return deferred;
}
})
Q.all(qs).then(function(){
console.log(results )
});
or use yield and co in new standard
https://github.com/kriskowal/q
Related
I'm not familiar enough with AJAX calls to know how this does or doesn't work.
If I have an array A and I'm using A.filter() on it, how will AJAX calls within filter work? The array is being used to populate a template, all synchronously I believe.
// Event calls function to filter list on page.
// Function calls filterArray(arrayList, objFilters)
async_fetch: function(string)
{
// Utilizes $.ajax() to retrieve a JSON array
var deferred = $.Deferred();
$.ajax({
url: ...,
dataType: "json",
success: function(data) {
var response = data;
deferred.resolve(data);
},
error: function(data)
{
//...
deferred.reject(msg);
}
});
return deferred;
};
filterArray: function(list, filters)
{
var filteredList = list.filter(function(item) {
for(var key in filters) {
// Actions for each of multiple filters to compare against...
else if(key == 'FILTER_X') {
var data = async_fetch(item.name);
// Use data to arrive at a determination, where true means I don't want to include this item in the filtered list
if(determination)
return false;
}
}
};
return filteredList;
};
// Results of filterArray() are passed along to a template within Backbone
// to redraw a segment of HTML on the page.
Will the call to filter just wait synchronously for the AJAX call to finish? Will the list get filtered otherwise and returned, and the AJAX call have to hook into the filtered list, and essentially finish the filtering later?
Should I just build a version of async_fetch() that isn't async?
You will need to .then() or .done() the call, eg
....
async_fetch(item.name).then(function(data){
if (data.determination)
// do something
})
....
Hi you can resolve the promise after your filtering the data.
example like,
hope this help you.
$(document).ready(function(){
function async_fetch(string)
{
// Utilizes $.ajax() to retrieve a JSON array
var deferred = $.Deferred();
$.ajax({
url: string,//your URL
dataType: "json",
success: function(data) {
var filterdData= filterArray(data);
deferred.resolve(filterdData);
},
error: function(data)
{
//...
deferred.reject(msg);
}
});
return deferred;
};
function filterArray(data)
{
var filteredList = data.filter(function(item) {
//filter whatever you want
})
return filteredList;
}
async_fetch(url).then(function(response){
//now you will get the filterd data
console.log(response);
})
});
You can do this with help of async/await as below:-
const filterArray = async function (list, filters) {
var filteredListPromise = list.filter(async function (item) {
for (var key in filters) {
// Actions for each of multiple filters to compare against...
if (key == 'FILTER_X') {
return arriveAtDetermination(item.name);
}
else {
//other filters
}
}
});
return Promise.all(filteredListPromise);
};
async function arriveAtDetermination(name) {
let data = await async_fetch(name);
return determination ? true : false;//your logic
}
//Now you can filter like
filterArray(list, filters).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
})
I am relatively a newbie to jquery and ajax and am trying to use the concept of deferrals and promises to solve this problem I have.
I would like to do the following:
Call a list of URLS and process the result returned from the urls. I would like to first process the results in parallel, and then combine the processed results to give me a final result.
Th pseudo-code is as follows:
var deferredAjaxCalls = [];
for (var i = 0; i < jobsListLength; i++) {
deferredAjaxCalls.push(
$.ajax({
url:"/myurl",
method:"POST",
contentType:"application/json",
dataType:"json",
data:mydata,
success:function(result){
//Some code here that is performance intensive
}
});
}
$.when.apply(this,deferredAjaxCalls).done(function(){
for (var k=0; k< arguments.length;k++){
//combine the results of the individual results of the
// success part of all the ajax calls and execute some more
//code synchronously
}
}).fail( function (jqXHR, status, error) {
//Log failed status
});
Initially, I moved all of the code from the success part inside the $.when.apply().However, this resulted in very slow performance as there is a lot of intensive computation that is now executed synchronously. So I am looking for a way to execute part of the code independently, and the final piece synchronously
I did read about using promises, but could not find any example where promises are used with an array of ajax calls with intermediate processing before finally synchronising in the when.apply() block
What would be a good way to solve this problem?
Thanks!
Starting with an array jobsList, you probably want something like this :
var deferredAjaxCalls = jobsList.map(function(job) {
return $.ajax({
url: "/myurl",
method: "POST",
contentType: "application/json",
dataType: "json",
data: mydata
}).then(process);// where `process` is a function that accepts $.ajax's (data, textStatus, jqXHR) and returns a *single* value/object - the result of the processing. This will standardise the data delivered below by $.when() to its success handler.
});
$.when.apply(null, deferredAjaxCalls).then(function() {
// Due to `.then(process)` above, `arguments` are guaranteed to comprise one arg per ajax call.
// Otherwise you potentially have the problem reported here - http://stackoverflow.com/questions/12050160/
for (var k=0; k<arguments.length; k++) {
// Combine the results of the individual results of the success part of all the ajax calls and execute some more code synchronously.
}
// In this function deliver an error by returning `$.Deferred().reject(new Error('myReason'))`
return combined_result;
}, function(jqXHR, status, error) {
// This hander will receive multiple $.ajax() params, which are best normalised into a single Error object.
return new Error(status); // similar to .then(process) above, reduce $.ajax's error args to a single "reason".
}).then(null, function(err) {
// All errors delivered by code above arrive here as a js Error.
// But please note that, in jQuery <v3.0, any uncaught errors above will genuinely throw (to the console).
console.log(err.message);
});
You can try using deferreds:
var req_responses = [];
var deferreds = [];
for(var i in jobs) {
deferreds[i] = new $.Deferred();
}
for(var i in jobs) {
(function(i) {
$.ajax ({
url: ".",
type: "POST",
dataType: "json",
done: function(response) {
//process the response
req_responses[i] = response;
deferreds[i].resolve();
}
});
})(i);
}
$.when.apply(deferreds).then(function(os) {
//all the responses are in req_responses
//finish processing
alert("done");
});
Here's an contrived example of what's going on: http://jsfiddle.net/adamjford/YNGcm/20/
HTML:
Click me!
<div></div>
JavaScript:
function getSomeDeferredStuff() {
var deferreds = [];
var i = 1;
for (i = 1; i <= 10; i++) {
var count = i;
deferreds.push(
$.post('/echo/html/', {
html: "<p>Task #" + count + " complete.",
delay: count
}).success(function(data) {
$("div").append(data);
}));
}
return deferreds;
}
$(function() {
$("a").click(function() {
var deferreds = getSomeDeferredStuff();
$.when(deferreds).done(function() {
$("div").append("<p>All done!</p>");
});
});
});
I want "All done!" to appear after all of the deferred tasks have completed, but $.when() doesn't appear to know how to handle an array of Deferred objects. "All done!" is happening first because the array is not a Deferred object, so jQuery goes ahead and assumes it's just done.
I know one could pass the objects into the function like $.when(deferred1, deferred2, ..., deferredX) but it's unknown how many Deferred objects there will be at execution in the actual problem I'm trying to solve.
To pass an array of values to any function that normally expects them to be separate parameters, use Function.prototype.apply, so in this case you need:
$.when.apply($, my_array).then( ___ );
See http://jsfiddle.net/YNGcm/21/
In ES6, you can use the ... spread operator instead:
$.when(...my_array).then( ___ );
In either case, since it's unlikely that you'll known in advance how many formal parameters the .then handler will require, that handler would need to process the arguments array in order to retrieve the result of each promise.
The workarounds above (thanks!) don't properly address the problem of getting back the objects provided to the deferred's resolve() method because jQuery calls the done() and fail() callbacks with individual parameters, not an array. That means we have to use the arguments pseudo-array to get all the resolved/rejected objects returned by the array of deferreds, which is ugly:
$.when.apply($,deferreds).then(function() {
var objects = arguments; // The array of resolved objects as a pseudo-array
...
};
Since we passed in an array of deferreds, it would be nice to get back an array of results. It would also be nice to get back an actual array instead of a pseudo-array so we can use methods like Array.sort().
Here is a solution inspired by when.js's when.all() method that addresses these problems:
// Put somewhere in your scripting environment
if (typeof jQuery.when.all === 'undefined') {
jQuery.when.all = function (deferreds) {
return $.Deferred(function (def) {
$.when.apply(jQuery, deferreds).then(
// the calling function will receive an array of length N, where N is the number of
// deferred objects passed to when.all that succeeded. each element in that array will
// itself be an array of 3 objects, corresponding to the arguments passed to jqXHR.done:
// ( data, textStatus, jqXHR )
function () {
var arrayThis, arrayArguments;
if (Array.isArray(this)) {
arrayThis = this;
arrayArguments = arguments;
}
else {
arrayThis = [this];
arrayArguments = [arguments];
}
def.resolveWith(arrayThis, [Array.prototype.slice.call(arrayArguments)]);
},
// the calling function will receive an array of length N, where N is the number of
// deferred objects passed to when.all that failed. each element in that array will
// itself be an array of 3 objects, corresponding to the arguments passed to jqXHR.fail:
// ( jqXHR, textStatus, errorThrown )
function () {
var arrayThis, arrayArguments;
if (Array.isArray(this)) {
arrayThis = this;
arrayArguments = arguments;
}
else {
arrayThis = [this];
arrayArguments = [arguments];
}
def.rejectWith(arrayThis, [Array.prototype.slice.call(arrayArguments)]);
});
});
}
}
Now you can simply pass in an array of deferreds/promises and get back an array of resolved/rejected objects in your callback, like so:
$.when.all(deferreds).then(function(objects) {
console.log("Resolved objects:", objects);
});
You can apply the when method to your array:
var arr = [ /* Deferred objects */ ];
$.when.apply($, arr);
How do you work with an array of jQuery Deferreds?
When calling multiple parallel AJAX calls, you have two options for handling the respective responses.
Use Synchronous AJAX call/ one after another/ not recommended
Use Promises' array and $.when which accepts promises and its callback .done gets called when all the promises are return successfully with respective responses.
Example
function ajaxRequest(capitalCity) {
return $.ajax({
url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
success: function(response) {
},
error: function(response) {
console.log("Error")
}
});
}
$(function(){
var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
$('#capitals').text(capitalCities);
function getCountryCapitals(){ //do multiple parallel ajax requests
var promises = [];
for(var i=0,l=capitalCities.length; i<l; i++){
var promise = ajaxRequest(capitalCities[i]);
promises.push(promise);
}
$.when.apply($, promises)
.done(fillCountryCapitals);
}
function fillCountryCapitals(){
var countries = [];
var responses = arguments;
for(i in responses){
console.dir(responses[i]);
countries.push(responses[i][0][0].nativeName)
}
$('#countries').text(countries);
}
getCountryCapitals()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<h4>Capital Cities : </h4> <span id="capitals"></span>
<h4>Respective Country's Native Names : </h4> <span id="countries"></span>
</div>
As a simple alternative, that does not require $.when.apply or an array, you can use the following pattern to generate a single promise for multiple parallel promises:
promise = $.when(promise, anotherPromise);
e.g.
function GetSomeDeferredStuff() {
// Start with an empty resolved promise (or undefined does the same!)
var promise;
var i = 1;
for (i = 1; i <= 5; i++) {
var count = i;
promise = $.when(promise,
$.ajax({
type: "POST",
url: '/echo/html/',
data: {
html: "<p>Task #" + count + " complete.",
delay: count / 2
},
success: function (data) {
$("div").append(data);
}
}));
}
return promise;
}
$(function () {
$("a").click(function () {
var promise = GetSomeDeferredStuff();
promise.then(function () {
$("div").append("<p>All done!</p>");
});
});
});
Notes:
I figured this one out after seeing someone chain promises sequentially, using promise = promise.then(newpromise)
The downside is it creates extra promise objects behind the scenes and any parameters passed at the end are not very useful (as they are nested inside additional objects). For what you want though it is short and simple.
The upside is it requires no array or array management.
I want to propose other one with using $.each:
We may to declare ajax function like:
function ajaxFn(someData) {
this.someData = someData;
var that = this;
return function () {
var promise = $.Deferred();
$.ajax({
method: "POST",
url: "url",
data: that.someData,
success: function(data) {
promise.resolve(data);
},
error: function(data) {
promise.reject(data);
}
})
return promise;
}
}
Part of code where we creating array of functions with ajax to send:
var arrayOfFn = [];
for (var i = 0; i < someDataArray.length; i++) {
var ajaxFnForArray = new ajaxFn(someDataArray[i]);
arrayOfFn.push(ajaxFnForArray);
}
And calling functions with sending ajax:
$.when(
$.each(arrayOfFn, function(index, value) {
value.call()
})
).then(function() {
alert("Cheer!");
}
)
If you're transpiling and have access to ES6, you can use spread syntax which specifically applies each iterable item of an object as a discrete argument, just the way $.when() needs it.
$.when(...deferreds).done(() => {
// do stuff
});
MDN Link - Spread Syntax
I had a case very similar where I was posting in an each loop and then setting the html markup in some fields from numbers received from the ajax. I then needed to do a sum of the (now-updated) values of these fields and place in a total field.
Thus the problem was that I was trying to do a sum on all of the numbers but no data had arrived back yet from the async ajax calls. I needed to complete this functionality in a few functions to be able to reuse the code. My outer function awaits the data before I then go and do some stuff with the fully updated DOM.
// 1st
function Outer() {
var deferreds = GetAllData();
$.when.apply($, deferreds).done(function () {
// now you can do whatever you want with the updated page
});
}
// 2nd
function GetAllData() {
var deferreds = [];
$('.calculatedField').each(function (data) {
deferreds.push(GetIndividualData($(this)));
});
return deferreds;
}
// 3rd
function GetIndividualData(item) {
var def = new $.Deferred();
$.post('#Url.Action("GetData")', function (data) {
item.html(data.valueFromAjax);
def.resolve(data);
});
return def;
}
If you're using angularJS or some variant of the Q promise library, then you have a .all() method that solves this exact problem.
var savePromises = [];
angular.forEach(models, function(model){
savePromises.push(
model.saveToServer()
)
});
$q.all(savePromises).then(
function success(results){...},
function failed(results){...}
);
see the full API:
https://github.com/kriskowal/q/wiki/API-Reference#promiseall
https://docs.angularjs.org/api/ng/service/$q
How do I fetch data from an array of json urls and store their contents in an array
<script type="text/javascript">
urls = ["https://spreadsheets.google.com/feeds/list/1RsiDuydBBHyu4OBjlxq1YH6yT3qcJDMB6-YKU-xxd_k/od6/public/basic?hl=en_US&alt=json","https://spreadsheets.google.com/feeds/list/1Pk4KnjBtYsIJF65ZQFEdBLqPufPK-HSFqhZvmxjZD_E/od6/public/basic?hl=en_US&alt=json"];
local = {};
$.ajax({
url: urls,
cache: false,
context: local,
dataType: 'jsonp',
success: function(data){
console.log(data);
}
});
</script>
The console.log method does not return any data and it does not return an error. How do I obtain data from the 2 urls I've used
No.
The array will be converted to a string, which won't be any of the URLs that you want, so you'll usually end up with a 404 error (which you won't see because you don't have an error handler (and I think there might be issues with detecting errors on JSONP requests anyway)).
If you want to loop over an array of URLs and make an Ajax request for each one, then you'll have to write the loop yourself, and call $.ajax each time you go around it.
You might want to consider using an iterator that you bump in the success function rather than using a simple for loop so that you don't attempt to fire off lots of requests in parallel.
jQuery doesn't have an overload for this by default, but you can achieve the same thing using promises and performing one AJAX request for each URL in the array:
var urls = ["https://spreadsheets.google.com/feeds/list/1RsiDuydBBHyu4OBjlxq1YH6yT3qcJDMB6-YKU-xxd_k/od6/public/basic?hl=en_US&alt=json","https://spreadsheets.google.com/feeds/list/1Pk4KnjBtYsIJF65ZQFEdBLqPufPK-HSFqhZvmxjZD_E/od6/public/basic?hl=en_US&alt=json"];
var $deferredArray = $.map(urls, function(url) {
console.log("here");
return $.ajax({
url: url,
cache: false,
success: function(data){
console.log("Success");
}
});
});
$.when.apply($, $deferredArray).then(function() {
console.log('All done');
});
You can use $.when.apply* for this. It allows you to pass in an array of promises and run a function when they have both completed. So something like this:
var urlArray = [url, url2];
var promises = [];
function returnPromise(url) {
return $.ajax({
url: url,
cache: false,
context: local,
dataType: 'jsonp'
});
}
// push each promise into your promises array
for (var i = 0, l = urlArray; i < l; i++) {
promises.push(returnPromise(urlArray[i]));
}
// pass the promises array into $.when
$.when.apply(null, promises).then(function (data1, data2) {
// do something with the data
});
*apply is available on every JS function and allows you to call it with an array of arguments instead of a list.
I am trying to wait for an ajax call to finish saving a model before saving the next model in the list. I was googling around and saw some stuff about deferred objects which are new to me, and another answer that had a recursive function do it. I tried the recursive method because it seemed to make a little more sense than with deferred objects and using $.when.apply($, arrayOfAjaxCalls).then(). So that code (the recursive one, looks like:
saveModel(index, numRequests) {
var self = this;
if (index < numRequests) {
var sample = self.samplesToSave[index];
return $.ajax({
url: model.url,
contentType: "application/json",
type: "POST",
data: JSON.stringify(model),
crossDomain: $.support.cors,
xhrFields: {
withCredentials: $.support.cors,
},
success: function(data) {
console.log("JUST SAVED");
console.log(data);
},
error: function(xhr: any) {
console.log(xhr);
},
}).then(() => {
self.saveModel(index + 1, numRequests);
});
}
}
I call this like:
saveModel(0, _.size(myCollection)
It doesn't actually wait for the ajax call to finish in its current state before calling the next saveModel. It basically just synchronously calls saveModel for each item in the collection in order. Any thoughts on what I'm missing? If there's a better solution with $.Deferred, I'm ok with that as well. Thanks.
Edit: Sorry it meant to say saveModel in the last line of the saveModel function. Was trying to get rid of parts that were domain specific. And I'm using typescript, not coffeescript
New attempt:
saveSampleNew() {
var d = $.Deferred();
d.resolve();
var p = d.promise();
var self = this;
self.samplesToSave.forEach(sample => p = p.then(() => self.makeSaveRequest(sample)));
return p;
}
makeSaveRequest(sample) {
var self = this;
return $.ajax({
url: "samples",
contentType: "application/json",
type: "POST",
data: JSON.stringify(sample),
crossDomain: $.support.cors,
xhrFields: {
withCredentials: $.support.cors,
},
success: function(data) {
console.log("SAVED12");
console.log(data);
},
});
}
Because this code depends on other async calls from completing, I call this new attempt like this:
this.saveContainers(project).then(() => {
}).done(() => {
self.saveSampleNew();
});
No, it should work this way. If you think it doesn't wait, please provide more information on how you call it and how experience that it does synchronously recurse.
There is one catch however with the recursive call:
.then(function() {
self.saveModel(index + 1, numRequests);
})
The promise that is returned by then, and subsequently by your saveModel method, does resolve directly with the first ajax call, it does not wait for the recursive chain. The other ajax calls are still happening (sequentially, as expected), but are not being tracked by the resulting promise.
To get that, and properly chain the promises so that it resolves with the result of the last ("innermost") promise, you will need to return the promise from the callback to then:
.then(function() {
return self.saveModel(index + 1, numRequests);
})
I'd probably use a for loop rather than a recursive call here, generally - I find those easier to read in this context.
saveModel() {
var d = $.Deferred(); d.resolve();
var p = d.promise(); // to start the chain
this.samplesToSave.forEach(sample => p = p.then(() => makeSaveRequest(sample));
return p;
}
makeSaveRequest(sample) {
return $.ajax({...}); // make request using `sample` as data
}