Handle Ajax responses in same order as Ajax requests - javascript

I would like to secure my code to make sure several Ajax requests triggered by user actions in a certain order will have their responses handled in the same order.
I want to keep the asynchonous mechanism of Ajax. Just secure my code to avoid out of sequence responses that can lead to mess on my web page.
To make it perfectly clear. Let's take an example:
$('#button1').click(function(){
$.ajax({url: 'dosomething1.html'})
.done( function(){ console.log('something 1 success'); } )
.fail( function(){ console.log('something 1 failure'); } );
});
$('#button2').click(function(){
$.ajax({url: 'dosomething2.html'})
.done( function(){ console.log('something 2 success'); } )
.fail( function(){ console.log('something 2 failure'); } );
});
$('#button3').click(function(){
$.ajax({url: 'dosomething3.html'})
.done( function(){ console.log('something 3 success'); } )
.fail( function(){ console.log('something 3 failure'); } );
});
If the user clicks on "#button1" then "#button2" and then "button3", I want to see in the console:
>something 1 success
>something 2 success
>something 3 success
It can happen that the responses are not received in the order the server sent them. So I want to get prepared for this scenario.
Note: I can't know in advance the sequence of events triggered by the user. So I need to find a way to chain the response handlers "on the fly".
What would be the best solution to achieve this?
I'm new to Ajax tricky stuff and I read a large amount of things today without finding THE solution (I guess that somehow deferred and promise objects could do the trick).
Please help me to get rid of this terrible headache. :)
Edit to comment the solution by Kevin B.
I struggled with my brain to FULLY understand the example from Kevin B (that does work) until I read a book on Deferreds that explains that the "then" function is actually creating a new Deferred and returns its promise.
This is this new promise that is "chained" to the previous one. It calls its successfull of failure callbacks depending on the result of the previous promise evaluation (resolved or rejected).
In our case that means that when the previous promise is evaluated, the "then" promise is also evaluated and takes as an input the result (resolved or rejected) of the previous promise to decide which callback to call.
In kevin B's code the ajax request promise is returned in both cases (resolved or rejected).
Hence, the .fail and .done callback of the promise are called ONLY ONCE the "then" promise is evaluated AND the returned promise (ajax request one) is "resolved" (.done function) or rejected (.fail function).
To go further:
My understanding is that the promise is a kind of listener on an event that can potentially happen in the future.
In classical cases, when the event happens, the deferred is changed to "resolved" or "rejected" state and the promise callbacks are called.
The promise is "listening" to the state of the deferred to be changed. The event trigerring this state change is the resolution or rejection of the initial event (ajax request, timeout, other...).
In "then" cases, the trigerring event for evaluating the promise is: the referenced promise (previous promise in the chain) is evaluated (either resolved or rejected).
Given the evaluation result, the success or failure callback is called.
I propose this slightly re-organized code inspired by Kevin's code to help out dummies like me to better understand:
var currentPromise = $.Deferred().resolve().promise();
$('#button1').click(function(){
var button1Promise = $.ajax({url: 'dosomething1.html'})
var thenPromise = currentPromise.then(
function () { return button1Promise;},
function () { return button1Promise;}); // to also handle fail conditions gracefully
currentPromise = thenPromise;
// "thenPromise" callback functions are returning the promise linked to
// the ajax request. So this promise is "replacing" the "thenPromise".
// Hence "done" and "fail" functions are finally defined for the ajax request promise.
thenPromise.done( function(){ console.log('something 1 success'); } );
thenPromise.fail( function(){ console.log('something 1 failure'); } );
});
Hopefully it will help people not totally comfortable with jquery concepts to fully understand promises chaining with "then" function.
Don't hesitate to comment if I misundertood something.

If you create a promise up front, you could keep chaining off of it to get your desired effect.
// ajax mock
function sendRequest(v, delay) {
var def = $.Deferred();
setTimeout(function () {
def.resolve(v);
}, delay);
return def.promise();
}
var ajaxPromise = $.Deferred().resolve().promise();
var delay = 600; // will decrement this with each use to simulate later requests finishing sooner
// think of this as a click event handler
function doAction (btnName) {
delay -= 100;
var promise = sendRequest(btnName, delay);
ajaxPromise = ajaxPromise.then(function () {
return promise;
}).done(function () {
console.log(btnName);
});
}
doAction("1");
doAction("2");
doAction("3");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Since i delayed them by 500ms, 400ms, and 300ms, none of them logged until after 500ms.
Here's your code with the same technique:
var ajaxPromise = $.Deferred().resolve().promise();
$('#button1').click(function(){
var promise = $.ajax({url: 'dosomething1.html'})
ajaxPromise = ajaxPromise.then(function () {
return promise;
}, function () {
return promise; // to also handle fail conditions gracefully
}).done( function(){ console.log('something 1 success'); } )
.fail( function(){ console.log('something 1 failure'); } );
});
// repeat for other two buttons
The important thing is all of the ajax requests will always be sent immediately, but the done and fail handlers won't be executed until their turn.

I can't know in advance the sequence of events triggered by the user.
So I need to find a way to chain the response handlers "on the fly".
You need to pipe ajax requests to have the respective responses in the same order, one way to do that is using this plugin https://code.google.com/p/jquery-ajaxq/

As you say:
I can't know in advance the sequence of events triggered by the user. So I need to find a way to chain the response handlers "on the fly".
The right way to go about this is definitely to use deferred objects / promises and NOT set the async parameter to false, which can cause a lot of unwanted problems.
Read the canonical introduction on how to do it here.
EDIT:
An example of synchronizing parallel tasks with $.when(), taken from here:
var promiseOne, promiseTwo, handleSuccess, handleFailure;
// Promises
promiseOne = $.ajax({ url: '../test.html' });
promiseTwo = $.ajax({ url: '../test.html' });
// Success callbacks
// .done() will only run if the promise is successfully resolved
promiseOne.done(function () {
console.log('PromiseOne Done');
});
promiseTwo.done(function () {
console.log('PromiseTwo Done');
});
// $.when() creates a new promise which will be:
// resolved if both promises inside are resolved
// rejected if one of the promises fails
$.when(
promiseOne,
promiseTwo
)
.done(function () {
console.log('promiseOne and promiseTwo are done');
})
.fail(function () {
console.log('One of our promises failed');
});

The easiest way here will be to use async : false parameter for $.AJAX(), just to be sure that your requests run one after another.
http://api.jquery.com/jquery.ajax/

Related

How to execute common code after a Dojo Deferred object is resolved or rejected?

I have a question about dojo/Deferred. I'll start with the question, then go into more detail about what I'm doing:
Is there a way to execute the same lines of code regardless of the outcome of the deferred, sort of like a finally block in a try...catch statement? From what I've read, it doesn't seem like there is, but maybe I'm understanding the documentation wrong and wanted to verify that with the SO community.
Here's what I'm doing:
In Dojo 1.9 (also works in 1.8), I instantiate a dojox.widget.Standby (a loading overlay) for a ContentPane before loading some data. Once the deferred call has completed, I want to hide my overlay as shown below:
standby = new Standby({
... // standby props
});
this.addChild(standby);
standby.show();
queryResults = grid.store.query({
... // query props
});
queryResults.then(function (results) {
if (results) {
... // do something
}
standby.hide();
}, function (error) {
... // handle error
standby.hide();
});
This works fine; however, presumably, I could have some process to be implement after the deferred completes that takes up several lines of code instead of just a single line and I wouldn't want to duplicate those lines of code. An alternative would be to create a private function and just call it with a one-liner in each block, but if there's a better way, I'd rather take that route.
Thanks in advance!
You can use the always method of the Promises API to execute a function regardless of whether the underlying Deferred succeeds or fails.
queryResult
.then(onSuccess, onFailure)
.always(function() {
standby.hide();
});
This is a good question. A dojo/Deferred object will return another Deferred object when Deferred#then is called. This allows you to chain a differed with multiple callbacks that are fired in a serial order. Therefore, I believe you can do something like this:
queryResults.then(function (results) {
if (results) {
... // do something
}
}, function (error) {
... // handle error
}).then(function(data){
// This will be fired with data returned from the previous callback.
standby.hide();
});
You can see this example fiddle that illustrates a similar, albeit simple, use case where regardless of if the Deferred is rejected or resolved, the callback to the second Deferred#then is fired after the initial error/success callback.
var deferred = new Deferred();
deferred.promise.always( function() { alert('ciao'); } );

When should I reject a promise?

I'm writing some JS code that uses promises. For example, I open a form pop-up and I return a jQuery Deferred object. It works like this:
If the user clicks OK on the form, and it validates, the Deferred resolves to an object representing the form data.
If the user clicks Cancel, then the Deferred resolves to a null.
What I'm trying to decide is should the Deferred instead reject, instead of resolve? More generally, I'm wondering when should I resolve to something like a null object, and when should I reject?
Here's some code demonstrating the two positions:
// Resolve with null.
var promise = form.open()
.done(function (result) {
if (result) {
// Do something with result.
} else {
// Log lack of result.
}
});
// Reject.
var promise = form.open()
.done(function (result) {
// Do something with result.
})
.fail(function () {
// Log lack of result.
});
The semantics of your two strategies are not really the same. Explicitly rejecting a deferred is meaningful.
For instance, $.when() will keep accumulating results as long as the deferred objects it is passed succeed, but will bail out at the first one which fails.
It means that, if we rename your two promises promise1 and promise2 respectively:
$.when(promise1, promise2).then(function() {
// Success...
}, function() {
// Failure...
});
The code above will wait until the second form is closed, even if the first form is canceled, before invoking one of the callbacks passed to then(). The invoked callback (success or failure) will only depend on the result of the second form.
However, that code will not wait for the first form to be closed before invoking the failure callback if the second form is canceled.
Since it's user-controlled, I wouldn't treat it as a "failure". The first option seems cleaner.
Well, in both cases you would do something different, so i would say always either resolve it, or reject it. Do your form post on resolve, and on reject do nothing. Then, on always, close the form.
var promise = form.open()
.done(function (result) {
// Do something with result.
})
.fail(function () {
// Log lack of result.
})
.always(function() {
// close the form.
})
If you aren't rejecting on cancel, when are you ever rejecting at all? at that point, why use a deferred object? You could reject on input error, but then you would have to generate a whole new promise if you wanted to allow them to fix it.
Deferreds don't really seem like the right thing to use here. I'd just use events.

Will $.when not bother with a deferred AJAX object if it doesn't exist?

Just wondering before I start hacking away with my code. For example:
if (blahblah) {
$.ajax("randomthingy1");
}
if (blahblahblah) {
$.ajax("randomthingy2");
}
// Use jQuery to test when they've both finished. Obviously they won't always both finish, as they might not both exist, and none of them might exist either.
$.when($.ajax("randomthingy1"), $.ajax("randomthingy2"), function (stuff) {
// foo
}
// Might produce an error, as one might not exist. But will it move on and not bother?
Just wondering. And if it does bother to create an error and stop execution, is there a way to catch the error and continue?
.when() will only fire the done() handler if all Defered objects you passed in could get resolved. So in your instance, if one Ajax request fails for whatever reason, the mixed in Defered object will resolve to fail and your handlers bound through .when() -> done will not fire. But of course all your handlers bound to fail or always will fire in that case.
$.when( $.ajax({}), $.ajax({}) )
.done(function() {
// all promises (ajax requests) resolved successfully
})
.fail(function() {
// at least one promise (ajax request) failed
})
.always(function() {
// will always get fired
});
See http://api.jquery.com/category/deferred-object/
I'm not sure this answers your question, but here's how I deal with these kind of things:
var requests = [];
if (blahblah) {
requests.push( $.ajax("randomthingy1") );
}
if (blahblahblah) {
requests.push( $.ajax("randomthingy2") );
}
$.when.apply( $, requests ).then( function( ) {
// handle success
}, function( ) {
// handle error
});
This makes sure that the code will enter handler even if none of these conditions are satisfied, i.e. requests do not exist.
You can use this layout to ensure you always respond to the deferred finishing, whether or not if was resolved or rejected:
$.when(deferred1, deferred2, ...)
.done(function (data1, data2, ...) {
// success handlers - fires if all deferreds resolve
})
.fail(function (error1, error2, ...) {
// failure handlers - fires if one or more deferreds reject or throw exceptions
})
.always(function () {
// complete handlers - always fires after done or fail
});

jQuery Deferred's, $.when() and the fail() callback arguments

I'm getting an unexpected result when using $.when() when one of the deferred operations does not succeed.
Take this JavaScript, which created 2 deferreds. The first one succeeds and the second one fails.
var f1 = function() {
return $.Deferred(function(dfd) {
dfd.resolve('123 from f1');
}).promise();
};
var f2 = function() {
return $.Deferred(function(dfd) {
dfd.reject('456 from f2');
}).promise();
};
$.when(f1(), f2())
.then(function(f1Val, f2Val) {
alert('success! f1, f2: ' + JSON.stringify([f1Val, f2Val]));
})
.fail(function(f1Val, f2Val) {
alert('fail! f1, f2: ' + JSON.stringify([f1Val, f2Val]));
});
Run it yourself: http://jsfiddle.net/r2d3j/2/
I get fail! f1, f2: ["456 from f2", null]
The problem is that in the .fail() callback the value passed with the f2() rejection, is being routed to the first argument, where i expect the f1Value. Which means that I don't really have a way of know which deferred object actually posted that reject(), and I also dont know which operation that failure data actually belongs to.
I would have expected that .fail() would get arguments null, '456 from f2' since the first deferred did not fail. Or am I just not doing deferreds right way here?
How do I know which deferreds failed, and which rejection arguments belong to which failed deferred if the argument order in the callback is not respected?
$.when() will execute the failed callback (2nd parameter passed to then()) immediately if any one of the parameters fails. It's by design. To quote the documentation:
http://api.jquery.com/jQuery.when/
In the multiple-Deferreds case where one of the Deferreds is rejected, jQuery.when immediately fires the failCallbacks for its master Deferred. Note that some of the Deferreds may still be unresolved at that point. If you need to perform additional processing for this case, such as canceling any unfinished ajax requests, you can keep references to the underlying jqXHR objects in a closure and inspect/cancel them in the failCallback.
There's actually no built-in way of getting a callback that waits untils all of them are finished regardless of their success/failure status.
So, I built a $.whenAll() for you :)
It always waits until all of them resolve, one way or the other:
http://jsfiddle.net/InfinitiesLoop/yQsYK/51/
$.whenAll(a, b, c)
.then( callbackUponAllResolvedOrRejected );
Internally, the "reject" and "fail" paths are handled by two totally separate queues, so it just doesn't work the way you expect.
In order to know which original Deferred failed from the "when()" group, you could have them pass themselves along with the ".reject()" call as part of an object literal or something.
I've faced this same problem, and I dealt with it by using the .always callback and inspecting my array of deferred objects. I had an unknown number of ajax calls, so I had to do the following:
// array of ajax deletes
var deletes = [];
$checkboxes.each(function () {
deletes.push(deleteFile(this));
});
$.when.apply($, deletes)
.always(function () {
// unfortunately .fail shortcircuits and returns the first fail,
// so we have to loop the deferred objects and see what happened.
$.each(deletes, function () {
this.done(function () {
console.log("done");
}).fail(function () {
console.log("fail");
});
});
});
The deleteFile method returns a promise, which has .done or .fail callbacks.
This allows you to take action after all deferreds have completed. In my case I'm going to show a delete file error summary.
I just tried this, and unfortunately I had to put a interval timer to check that they were all truly done after my $.each on the deferred objects. This seems odd and counterintuitive.
Still trying to understand these deferreds!
Very old question but now, to wait until all are resolved, you can use Promise.allSettled since $.ajax deals with standard promises.
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise
Therefore you can use
Promise.allSettled([$.ajax(), $.ajax()])
.then((res) => {
if (res[0].status === 'fulfilled') {
console.log(res[0].value)
// do something
} else {
console.error('res1 unavailable')
}
if (res[1].status === 'fulfilled') {
console.log(res[1].value)
// do something
} else {
console.error('res2 unavailable')
}
})

timeout for function (jQuery)

function getNames(){
// some code
}
This function can be done in one second, but sometimes it freezes itself and html block on the page (ajax inside) on infinite time.
I would like to have time limit for this function. If it doesn't finish in ten seconds, then abort it.
How to do this?
This is really easy with jQuery 1.5’s deferred promises.
The following example creates a Deferred and sets two timer-based functions to either resolve or reject the Deferred after a random interval. Whichever one fires first “wins” and will call one of the callbacks. The second timeout has no effect since the Deferred is already complete (in a resolved or rejected state) from the first timeout action.
// Create a Deferred and return its Promise
function asyncEvent() {
var dfd = new jQuery.Deferred();
setTimeout(function() {
dfd.resolve('hurray');
}, Math.floor(Math.random() * 1500));
setTimeout(function() {
dfd.reject('sorry');
}, Math.floor(Math.random() * 1500));
return dfd.promise();
}
// Attach a done and fail handler for the asyncEvent
$.when( asyncEvent() ).then(
function(status) {
alert( status + ', things are going well' );
},
function(status) {
alert( status + ', you fail this time' );
}
);
You can easily modify this example to suit your needs:
// Create a Deferred and return its Promise
function asyncEvent() {
var dfd = new jQuery.Deferred();
// Your asynchronous code goes here
// When the asynchronous code is completed, resolve the Deferred:
dfd.resolve('success');
setTimeout(function() {
dfd.reject('sorry');
}, 10000); // 10 seconds
return dfd.promise();
}
// Attach a done and fail handler for the asyncEvent
$.when( asyncEvent() ).then(
function(status) {
alert( status + ', things are going well' );
},
function(status) {
alert( status + ', you fail this time' );
}
);
If it is the function itself that's grinding away, just set a timer and either return or throw if it exceeds your maximum time period.
If, on the other hand, if the delay is caused by the AJAX request not returning, and you are using $.ajax(), you can always set the timeout setting. It should kill the request. Note that you need to avoid relying on the success callback, since it only gets called if the request succeeds. Instead, use complete callback to watch for failures. It will tell you if an error occurred. According to the documentation, this is available in 1.4 onwards. I'm not sure about pre-1.4, but I think it worked in 1.3 as well.
As suggested above, you can go with Web Workers if //some code is something that might take a long time sometimes (like waiting for large amounts of information from a slow server). That will allow background processing without locking the main page. Note that not all browsers support it yet.
You can find a nice introduction here.

Categories