jQuery when - wait for multiple success/done callbacks - javascript

I´m using $.when to determine, when an array of ajax promises are finished. I encountered, that the moment $.when fires the ajax calls are finished, but their callbacks / done functions aren´t. How can I wait for the callbacks to be finished?
the ajax calls look like this:
$.ajax({
method: 'POST',
url: url,
data: formData,
processData: false,
contentType: false
}).then(
function(data) {
data = JSON.parse(data);
var url = data.url;
obj.set('src', url);
});
and $.when s.th. like this:
$.when(promises).done(function(){
// the values of objs change in the .done function of the request above
// but when the following ajax got fired, the values haven´t changed yet
if(DEV) console.info('send Json: ', objs);
$.ajax({
method: "POST",
url: url,
data: objs
});
});

I would do this by using jQuery v1.8 or later and using then rather than fail and done and success/failure callbacks:
var promise1 = $.ajax({
/* ...params...*/
}).then(
function(data) {
// Handle success; if you modify what you get, `return` it
// and it will get propagated
return /*...`data` or updated `data` as appropriate...*/;
},
function(error) {
// Handle failure
}
);
var promise2 = /*...*/;
Then
$.when(promise1, promise2).then(
function(data) {
// Handle overall success; this will be called after the
// earlier `then` callbacks on each request
},
function(error) {
// Handle failure
}
);
Live example on jsFiddle (sadly, Stack Snippets don't provide any ajax features); full source below
Note that $.when expects discrete arguments, not an array. If you really have an array, then:
$.when.apply($, promises).then(
// ...
);
...or of course on a modern JavaScript engine:
Promise.all(promises).then(
// ...
);
Full source of the fiddle above, which uses jQuery v2.2.4:
log("Starting");
var promise1 = $.ajax({
url: "/echo/json/"
}).then(
function(data) {
// Handle success; if you modify what you get, `return` it
// and it will get propagated
log("Got promise1's response, returning 'p1'");
return 'p1';
},
function(error) {
// Handle failure
}
);
var promise2 = $.ajax({
url: "/echo/json/"
}).then(
function(data) {
// Handle success; if you modify what you get, `return` it
// and it will get propagated
log("Got promise2's response, returning 'p2'");
return 'p2';
},
function(error) {
// Handle failure
}
);
$.when(promise1, promise2).then(
function(result1, result2) {
// Handle overall success; this will be called after the
// earlier `then` callbacks on each request
log("Both are done", result1, result2);
},
function(error) {
// Handle failure
}
);
function log() {
// Old-fashioned to stay ES5 compatible
$("<pre>").text(Array.prototype.join.call(arguments, ", ")).appendTo(document.body);
}

Can you chain your promises? With something like this (not tested):
$.ajax({
method: 'POST',
url: url,
data: formData,
processData: false,
contentType: false
}).then(
function(data) {
data = JSON.parse(data);
var url = data.url;
obj.set('src', url);
// return your promises (if possible, I don't know where they come from)
return promises
}).then(
function() {
if(DEV)
console.info('send Json: ', objs);
$.ajax({
method: "POST",
url: url,
data: objs
});
});

use this
$.ajax({
method: 'POST',
url: url,
data: formData,
processData: false,
contentType: false,
success: function(data){/* to handle success response */},
error: function(error){/* to handle error response */},
complete: function(data){/* to handle the response success or error*/}
});

the data = JSON.parse(data); in your ajax function is not mutating the data object but create a new data object, you need to return it back so the caller can grab it
Try it like this,
function getData(){
return $.ajax({
method: 'POST',
url: url,
data: formData,
processData: false,
contentType: false
}).then(
function(data) {
data = JSON.parse(data);
var url = data.url;
obj.set('src', url);
return data;
});
}
var data = $.when(promises).done(getData);

Related

jQuery AJAX stop request after another response

i have an issue and i need an idea for solve :)
I have 2 call to $.ajax
First, is asynch, and during a lot of time (1 minutes for example)
Second, is sync (in ajax async: false) and it response fast (5 sec for example)
Second call is in a loop (requests->response->print data, request->response->print data).
I need when first finish (success or error), stop second call.
I attach an example code:
var success = false;
$.ajax({
type: "POST",
url: urlRest,
data: {
data: dataSend
},
success: processOK,
error: processError
});
do {
$.ajax({
type: "POST",
url: urlData,
data: {
data: dataSend
},
async: false,
success: function(data, textStatus, jqXHR){
console.log(data);
},
error: function(data, textStatus, jqXHR){
console.log("Error");
}
});
} while (!success);
I hope it's clear :)
I corrected an issue that would cause some errors, try this out.
let printData = function( input ){
let config = {
urlRest: '',
data: { data: {} },
loop: false,
callback: false
}
$.each(config,function(k,v){ config[k] = input[k] });
config.loop = false;
$.ajax({
type: 'POST',
url: config.urlRest,
data: config.data,
success: function( data ){
// Based on the response if you need to run again change config.loop to true and it will run again
// you can also alter anything your sending through
if( config.loop ) printData( config );
else if( typeof config.callback === 'function' ) callback();
},
error: function(){
// Based on the response if you need to run again change config.loop to true and it will run again
// you can also alter anything your sending through
if( config.loop ) printData( config );
else if( typeof config.callback === 'function' ) callback();
}
});
}
printData({
urlRest: '', // URL Here
data: data, // Data Object
loop: true, // Set this to true if you want it to loop
callback: function(){
console.log( 'Job Complete' );
}
})
You can run async calls in synchronous manner using SynJS:
function ajaxWrapper(ctx, url, data){
var res={done:false};
$.ajax({
type: "POST",
url: url,
data: data,
success: function(result){
res.data=result;
},
error: function(){
res.error=true;
},
}).always(function(){
res.done = true;
SynJS.resume(ctx); // <-- tell caller that callback is finished
});
return res; // <-- return object that will hold the results
}
// function that is executed in synchronous manner
function myFunc(modules, urlRest, urlData) {
var success = false;
var res1 = modules.ajaxWrapper(_synjsContext, urlRest, urlData);
SynJS.wait(res1.done); // <-- wait for result from callback
do {
var res2 = modules.ajaxWrapper(_synjsContext, urlRest, urlData);
SynJS.wait(res2.done); // <-- wait for result from 2nd callback
} while (!success);
}
var modules = {ajaxWrapper: ajaxWrapper};
SynJS.run(myFunc,null, modules, "/", {}, function () {
console.log('done');
});
You can change the success value like this
$.ajax({
type: "POST",
url: urlRest,
data: {
data: dataSend
}
}).always(function() {success=true;});
Or you can create a self call function (after the second ajax finish, calls it again) but before the call its checks the success variable like #mplungjan did.
It is never a good idea to loop Ajax. You need to allow the call to return.
Here is an example that is NOT using async false
var firstDone = false,tId;
// call long ajax
$.ajax({
type: "POST",
url: urlRest,
data: {
data: dataSend
}
}).done(processOK);
}).fail(processError)
}).always(function() {firstDone=true; clearTimeout(tId);}); // stops the other loop
// setup function that can be looped
function callAjax() {
if (firstDone) return;
$.ajax({
type: "POST",
url: urlData,
data: {
data: dataSend
}
}).done(function(data, textStatus, jqXHR) {
console.log(data);
}).fail(function(data, textStatus, jqXHR) {
console.log("Error");
}).always(function() {
tId=setTimeout(callAjax,1000); // give the server time to recover
});
}
callAjax();

AJAX promise call handling

I have an Ember promise call as below;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({
//want this common
url: requestUrl,
type: type, // HTTP method
dataType: dataType, // type of data expected from the API response
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(postData)
})
.done(function(data, status, xhrObject) {
//But want this to be different
// resolve call
})
.fail(function(xhrObject, status, error){
// reject call
});
})
My question is can I use common code for $.ajax(), but have different implementation for done() callback
I can check that by passing some parameter from the calling place.
so basically, I want
if (someparam == 'handleDone1')
call resolve(data)
else
call resolve({data})
You are currently passing a function to done by hard coding a function expression into it.
Replace that with a variable. Pass a value to that variable as a function argument.
Alternatively, don't use done here at all. Just return the return value of $.ajax() and call done() on that in the calling function.
return a promise instead of ajax call. And wrap the ajax call into promise.
checkout the below code. It may helps.
function someFunction(resolve1, reject1) {
return new Ember.RSVP.Promise(function(resolve, reject) {
$.ajax({
//want this common
url: requestUrl,
type: type, // HTTP method
dataType: dataType, // type of data expected from the API response
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(postData)
})
.done(function(data, status, xhrObject) {
//But want this to be different
// resolve call
var dicision = resolve1();
if(dicision){
resolve(data);
} else {
resolve({data});
}
})
.fail(function(xhrObject, status, error){
// reject call
});
}

Can I write a JS function that: returns as a normal function would or wait and return AJAX results?

Currently, I have a function getData that makes an AJAX call for some data, stores it in localStorage, and then calls the callback. If the data is already in localStorage, then getData will just call the callback.
// A wrapper around localStorage.
var storage = new Storage();
function getData(callback){
var KEY = 'key';
var data = storage.get(KEY);
if (data){
callback(data);
} else {
Utils.ajaxPost({
...,
success: function(data){
storage.set(KEY, data);
callback(data);
}
});
}
}
// I use getData like this.
getData(function(data){
// do something with data
});
I am wondering if there is a way I can write the getData function (perhaps with deferred/promises/closures) such that I can use it like this:
var data = getData();
// do something with data
Due to the asynchronous nature of AJAX, it is not possible to write:
function getData(){
var result;
$.ajax({
url: 'script.php',
type: 'POST',
dataType: 'html',
cache: false,
succes: function(data){
result = data;
}
});
return result;
}
var data = getData();
console.log(data); //undefined
This will result in data being undefined as the function will return before the request to the server completes.
You can get around this by setting async: false, but this is generally regarded as a bad idea, as it freezes the browser until it receives a response.
function getData(){
var result;
$.ajax({
url: 'submit.php',
type: 'POST',
dataType: 'html',
async: false,
success: function(data){
result = data;
}
});
return result;
}
var data = getData();
console.log(data); // Server's response
A better idea is to use callbacks (which you seem to already be doing)
getData(function(data){
// do something with data
});
Or promises:
function getData(){
return $.ajax({
url: 'submit.php',
type: 'POST',
dataType: 'html'
});
}
var data = getData()
.success(function(data){
console.log(data);
})
.fail(function(){
console.log("Error!");
});
Promises are a good solution for dealing with multiple callbacks and decoupling your code.
To make that work in your case, you'd have to alter your code somewhat to have the first part of the if statement also return a promise:
function getData(key){
var data = localStorage.getItem(key);
if (data){
return $.Deferred().resolve(data);
} else {
return $.ajax({
url: 'submit.php',
type: 'POST',
dataType: 'html',
cache: false
});
}
}
var data = getData("key").done(function(data){
localStorage.setItem("key", data);
console.log(data)
});
I'm not sure how much sense it makes to do it this way, though.
Summary: you can do what you are asking, but only using async: false, which is usually considered a bad idea. Here's how:
function getData(){
var KEY = 'key',
data = localStorage.getItem(KEY),
retVal;
if (data){
retVal = data;
} else {
$.ajax({
url: 'submit.php',
type: 'POST',
dataType: 'html',
async: false,
success: function(data){
retVal = data;
localStorage.setItem(KEY, data);
}
});
}
return retVal;
}
var data = getData();
console.log(data);
Very useful reference: How do I return the response from an asynchronous call?

How to pass arguments in promise

I am using promise like this:
var restClient = {
serveRequest: function(rUrl, type, body, rHeaders, rAsync, callback) {
var promise = jQuery.ajax({
url: rUrl,
type: type,
data: body,
headers: rHeaders,
async: rAsync,
contentType: "text/plain",
dataType: "json"
});
promise.then(onSuccess, onError);
},
onSuccess: function(data) {
callback(data);
},
onError: function(msg) {
console.log(msg.responseText);
}
}
How can I pass arguments (callback) in promise.then onSuccess? I want to use that in onSuccess method later.
I am using promise like this
Well, first of all, you shouldn't. The purpose of promises is to be returned as results from asynchronous functions, so that you don't need callback parameters any more. You'd better just do
var restClient = {
serveRequest: function(rUrl, type, body, rHeaders, rAsync) {
var promise = jQuery.ajax({
url: rUrl,
type: type,
data: body,
headers: rHeaders,
async: rAsync,
contentType: "text/plain",
dataType: "json"
});
return promise;
}
};
and let the caller of restClient.serveRequest(…) invoke .then(…).
How can I pass arguments (callback) in promise.then onSuccess?
You don't need that onSuccess. Just directly use
promise.then(callback, function(msg) {
console.log(msg.responseText);
});
I want to use that in onSuccess method later.
You cannot. It tries to use callback, but that is a parameter local to the serveRequest method - so onSuccess could at most be a local function in there, but not a method on its own.

Deferred calls for multiple ajax calls

I have the following JS methods:
var foo = function() {
var dfd = $.Deferred();
console.log('foo');
dfd.resolve();
return dfd.promise();
};
var ajaxCall1 = function () {
var dfd = $.Deferred();
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: 'xxxxxxx',
data: { },
success: function(response) {
dfd.resolve();
}
});
return dfd.promise();
};
var ajaxCall2 = function () {
var dfd = $.Deferred();
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: 'xxxxxxx',
data: {},
success: function (response) {
dfd.resolve();
}
});
return dfd.promise();
};
var ajaxCall3 = function () {
var dfd = $.Deferred();
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: 'xxxxxxx',
data: {},
success: function (response) {
dfd.resolve();
}
});
return dfd.promise();
};
and I am calling them via this code:
foo().done(function () {
return ajaxCall1();
}).done(function () {
return ajaxCall2();
}).done(function () {
return ajaxCall3();
});
The issue is that ajaxCall2 is called before the success of ajaxcall1 has occurred. Can you help me fixing this? I need to make ajax calls one by one when success of previous one has occurred.
Use the $.when
var deferredObject = $.ajax({});
$.when(deferredObject)
.then(function(){
var deferredobject2 = $.ajax({});
$.when(deferredobject2)
.then(function(){ alert('after 2nd ajax call');});
});
First of all, you can return the results of the $.ajax calls directly since they are promises already (no need for an intermediate Deferred):
var ajaxCall1 = function () {
return $.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: 'xxxxxxx',
data: { }
});
};
var ajaxCall2 = function () {
return $.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: 'xxxxxxx',
data: {}
});
};
var ajaxCall3 = function () {
return $.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: 'xxxxxxx',
data: {}
});
};
Second, what you wanna use is .pipe() to effectively chain the calls:
foo().pipe(function () {
return ajaxCall1();
}).pipe(function () {
return ajaxCall2();
}).pipe(function () {
return ajaxCall3();
}).done(function() {
// call1, call2 and call3 done in sequence
}).fail(function() {
// one of the ajax requests failed
});
Simplify.
function foo() {
var dfd = $.Deferred();
console.log('foo');
dfd.resolve();
return dfd.promise();
}
function ajaxCall1() {
return $.ajax({
type: 'POST',
dataType: 'json',
url: 'xxxxxxx',
data: { },
success: function(response) {
console.log('ajaxCall1 success');
}
});
return dfd.promise();
}
// and so on for ajaxCall2 and ajaxCall3
Enhance.
foo().done(function () {
ajaxCall1().done(function () {
ajaxCall2().done(function () {
ajaxCall3();
});
});
});
http://jsfiddle.net/mattball/LxjDS/
Further reading:
How to chain ajax calls using jquery
jQuery Deferred not calling the resolve/done callbacks in order
jQuery deferred - do I need pipes or chains to achieve this pattern?
Same as the other answer except simplifying the callbacks with Frame.js
var responses = [];
for(var i=0; i<1000; i++){
Frame(function(callback){
$.ajax('myserver.api', {
data:i,
type:'post',
complete:function(response) {
responses.push(response);
callback();
}
});
});
}
Frame.start();
Normally, slamming the browser with AJAX requests like this would cause the browser to hang, and the response variables would be returned in the order they are received, rather than the original order they were sent in. Adding Frame to the mix here sorts all that out.
Or you could just use it to flatten out the callbacks:
Frame(function(next){
foo().done(next);
});
Frame(function(next){
ajaxCall1().done(next);
});
Frame(function(next){
ajaxCall2().done(next);
});
Frame(function(next){
ajaxCall3().done(next);
});
Frame(function(next){
//do more stuff
next();
});
Frame.start();
I've had similar problems working heavily with SharePoint web services - you often need to pull data from multiple sources before you're able to continue working.
To solve it I embedded this kind of functionality into my AJAX abstraction library. You can easily define a request which will trigger a set of handlers when complete. However each request can be defined with multiple http calls. Here's the component:
DPAJAX at DepressedPress.com
This very simple example creates one request with three calls and then passes that information, in the call order, to a single handler:
// The handler function
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) };
// Create the pool
myPool = DP_AJAX.createPool();
// Create the request
myRequest = DP_AJAX.createRequest(AddUp);
// Add the calls to the request
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]);
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]);
// Add the request to the pool
myPool.addRequest(myRequest);
Note that unlike many of the other solutions provided this method does not force single threading of the calls being made - each will still run as quickly as the environment allows but the handler will only be called when all are complete. The component also supports user-defined numbers of requests so you can force single-threading easily if you like. It also supports the setting of timeout values and retry attempts if your service is a little flakey.
I've found it insanely useful (and incredibly simple to understand) for this kind of work.

Categories