I am working with a framework that uses jQuery Deferred Objects.
In my code, one async operation should executed successfully so that another async operation be executed.
function doOperation() {
var def2mock = $.Deferred();
var def1 = doSomeAsyncOperation().done(function () {
var def2 = doAnotherAsyncOperation();
});
return $.when(def1, def2mock);
}
So I put the call for the second operation in the "done" promise of the deferred of the first operation as in the example.
Now, I wrap this sequence with the function doOperation. The result of doOperation should be the join of the two deferreds of both async calls. Meaning doOperation succeeds if all async operations succeed and fails if any of them fail, which is exactly what $.when does.
The problem is that in order to create the join with $.when, I need both deferreds present at the time $.when is called. And because the deferred of the second async operation is not available at that moment, I had to find a way to create the join first, then add the deferred of the second async operation to the join later.
To do this, I thought I can define a new mock $.Deferred named def2 in doOperation. And when the real def2 is ready, I can somehow link the real deferred to the mock one, i.e. make the real one synchronize its state with mock one. The only way I found was to do
def2.done(def2mock.resolve)
// and
def2.fail(def2mock.reject)
but I don't feel this manual linking is the right way to do it.
So please tell me if you have a better suggestion on how to do it the right way.
And when the real def2 is ready, I can somehow link the real deferred to the mock one, i.e. make the real one synchronize its state with mock one.
This is actually what .then does: It returns a new promise which gets resolved/rejected when the promise returned from the passed callback is resolved/rejected.
In order to make this work with $.when, you have to keep a reference to the original promise:
function doOperation() {
var def1 = doSomeAsyncOperation();
var def2 = def1.then(function () {
return doAnotherAsyncOperation();
});
return $.when(def1, def2);
}
DEMO
def2 will be resolved when the promise returned by the callback is resolved, i.e. the one that doAnotherAsyncOperation returns.
But I don't think using $.when is a good choice here, from a conceptional point of view. You are actually executing two asynchronous functions sequentially, but $.when is used to run asynchronous functions in parallel.
I would just chain the function calls via .then and collect the responses:
function doOperation() {
return doSomeAsyncOperation().then(function (resp1) {
return doAnotherAsyncOperation().then(function(resp2) {
return [resp1, resp2];
});
});
}
DEMO
I'd say it's clearer here that doAnotherAsyncOperation is executed after doSomeAsyncOperation and the result of both calls is available to the caller of doOperation.
You might have to do something similar for the fail case.
I don't have any experience with Deferred (nor do I fully understand what it does), but if you are just looking to chain operations, would something like this work?
function doOperation(operations) {
var wrapper = {};
wrapper.index = 0;
wrapper.operations = operations;
wrapper.onthen = $.proxy(function() {
if (this.index < this.operations.length) {
this.operations[this.index]().then(this.onthen);
}
this.index++;
}, wrapper);
wrapper.onthen();
}
Here's a demonstration: http://jsfiddle.net/vz6D7/2/
Related
I have situation where I believe I need to create a Deferred object with a "then" handler, but wait until the "then" handler has completed it's own promise before moving on.
The use case is a record object, and the above function is it's save method. The record object has an attribute called saveQueue, which is set to $.Deferred() on the record's instantiation. The resolve call on saveQueue was supposed to make sure the Deferred there is always executing every new handler attached to it as soon as it could. The idea being that you can call save several times on the record in short succession, but the calls will run one after another, and not overlap.
I am using a Deferred to enqueue Ajax calls, so that one does not run until the previous one call finished. However, from the same method, I want to return a Deferred that can be resolved/rejected by the jQuery Ajax object, like so:
record.saveQueue = $.Deferred();
self.save = function( record ){
var deferredAction = $.Deferred();
deferredAction.then(function() {
return $.post("/example_save_endpoint");
});
record.saveQueue.always(function(){
deferredAction.resolve();
}).resolve();
return deferredAction;
}
However, when I use this code, the deferredAction promise always ends up resolved, presumably because the #then handler is returning a "pending" (and thus non-rejecting) promise. Is there any way to force the Deferred to wait until the Ajax promise is complete before resolving/rejecting? Or is there another, better way to thread this needle?
Your idea might work, but
the queue must not be resolved using .resolve() every time the method is called, instead it should be initialised only with a resolved promise.
to actually queue on the record.saveQueue, it needs to be changed (overwritten) on every method call, to represent the end of the latest request.
And we don't need any deferreds for that, as we can work with the promises that $.post returns.
So use this:
var emptyQueue = $.when(undefined); // an already fulfilled promise as the start
// equivalent: = $.Deferred().resolve().promise();
function startQueue() {
return emptyQueue; // yes, this delibaretely returns a constant, the begin
// of the queue always looks the same (and is never mutated)
}
// every time you create a record, do
record.saveQueue = startQueue();
// and use that in your methods:
this.save = function(record) {
var queuedRequestResult = record.saveQueue.then(function() {
return $.post("/example_save_endpoint");
// ^^^^^^ promises chain :-)
});
// Magic happens here:
record.saveQueue = queuedRequestResult // we swap the previous queue promise for a new
// one that resolves only after the request
.then(startQueue, startQueue); // and make sure it then starts with a fresh
// queue, especially when the request failed
//.then(null, startQueue) is similar, except unnecessarily remembering the last result
return queuedRequestResult;
}
I would probably choose not to do it this way, but a deferred/promise can indeed be used as a queuing device.
You need a slight(?) variation of what you already tried.
self.queue = $.when();//A resolved promise, used to form a queue of functions in a .then() chain.
self.save = function(data) {
var dfrd = $.Deferred();//A Deferred dedicated to this particular save.
self.queue = self.queue.then(function() {
return $.post("/example_save_endpoint", data) //Make the AJAX call, and return a jqXHR to ensure the downstream queue waits for this jqXHR to resolve/reject.
.then(dfrd.resolve, dfrd.reject) //Resolve/reject the Deferred for the caller's benefit
.then(null, function() {
//Force failure down the success path to ensure the queue is not killed by an AJAX failure.
return $.when();//Return a resolved promsie, for the queue's benefit.
});
});
return dfrd.promise();//allow the caller to do something when the AJAX eventually responds
}
For explanation, see comments in code
I would like to get a deeper understanding of how Promises work internally.
Therefore I have some sample code:
var p1 = new Promise(
function(resolve, reject) {
window.setTimeout(
function() {
resolve('res called')
}, 2000);
});
var p2 = new Promise(
function(resolve, reject) {
window.setTimeout(
function() {
resolve('res called')
}, 2000);
});
function chainPromises() {
return p1.then(function(val) {
console.log("p1");
return p2.then(function(val) {
console.log("p2");
return val;
});
});
}
chainPromises().then(function(val) {
console.log(val);
});
Here a link to execute this code.
As you would predict, first p1 is resolved, afterwards p2 and in the end the final then prints the resolv value.
But the API ref states the following:
"then" returns a new promise equivalent to the value you return from
onFulfilled/onRejected after being passed through Promise.resolve
So it would be interesting to know WHEN exactly the "then" function is executed?
Because the final "then" in the code is chained to the chainPromises(), I first thought that
it would execute after the function chainPromises() returns something (in this case another promise).
If this would have been the case the "val" of the final "then" function would be the returned promise.
But instead, the final "then" waits until all promises inside the first "then" which are returned have been resolved.
This absolutely makes sense because in this way, the "then" functions can be stacked, but
I do not really get how this is done, since the API spec. does not really cover what "then" returns and when the "then" functions is executed.
Or in other words, why does the final "then" function wait until all the Promises are resolved inside the chainPromises() function instead of just waiting for the first returned object as the API doc says.
I hope I could make clear what I mean.. :)
About Promise resolution
The thing you're witnessing here is called recursive thenable resolution. The promise resolution process in the Promises/A+ specification contains the following clause:
onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x)
The ES6 promise specification (promises unwrapping) contains a similar clause.
This mandates that when a resolve operation occurs: either in the promise constructor, by calling Promise.resolve or in your case in a then chain a promise implementation must recursively unwrap the returned value if it is a promise.
In practice
This means that if onFulfilled (the then) returns a value, try to "resolve" the promise value yourself thus recursively waiting for the entire chain.
This means the following:
promiseReturning().then(function(){
alert(1);
return foo(); // foo returns a promise
}).then(function(){
alert(2); // will only run after the ENTIRE chain of `foo` resolved
// if foo OR ANY PART OF THE CHAIN rejects and it is not handled this
// will not run
});
So for example:
promiseReturning().then(function(){
alert(1);
return Promise.resolve().then(function(){ throw Error(); });
}).then(function(){
alert("This will never run");
});
And that:
promiseReturning().then(function(){
alert(1);
return Promise.resolve().then(function(){ return delay(2000); });
}).then(function(){
alert("This will only run after 2000 ms");
});
Is it a good idea?
It's been the topic of much debate in the promises specification process a second chain method that does not exhibit this behavior was discussed but decided against (still available in Chrome, but will be removed soon). You can read about the whole debate in this esdiscuss thread. This behavior is for pragmatic reasons so you wouldn't have to manually do it.
In other languages
It's worth mentioning that other languages do not do this, neither futures in Scala or tasks in C# have this property. For example in C# you'd have to call Task.Unwrap on a task in order to wait for its chain to resolve.
Let's start with an easy perspective: "chainPromises" returns a promise, so you could look at it this way:
// Do all internal promises
var cp = chainPromises();
// After everything is finished you execute the final "then".
cp.then(function(val) {
console.log(val);
});
Generally speaking, when returning a promise from within a "then" clause, the "then" function of the encapsulating promise will be marked as finished only after the internal "then" has finished.
So, if "a" is a promise, and "b" is a promise:
// "a"'s "then" function will only be marked as finished after "b"'s "then" function has finished.
var c = a.then(function () {
return b.then(function () {
console.log("B!");
};
};
// c is a promise, since "then" always returns a promise.
c.then(function() {
console.log("Done!");
};
So the output will be:
B!
Done!
Notice btw, that if you don't "return" the internal promise, this will not be the case:
// "a"'s "then" function will only be marked as finished without waiting for "b"'s "then" to finish.
var c = a.then(function () {
// Notice we're just calling b.then, and don't "return" it.
b.then(function () {
console.log("B!");
};
};
// c is a promise, since "then" always returns a promise.
c.then(function() {
console.log("Done!");
};
Here we can't know what would be outputted first. It could be either "B!" or "Done!".
Please check the below example regarding how promises works:
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
console.log('person1: shoe ticket');
console.log('person2: shoe ticket');
const promiseGirlFriendBringingTickets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ticket');
}, 3000);
});
promiseGirlFriendBringingTickets.then((t) => {
console.log(`person3: show ${t}`);
})
console.log('person4: shoe ticket');
console.log('person5: shoe ticket');
Promise then return promise object, not promise's resolved value. I forked your JsFiddle, and added some of mine try this.
promise.then is executed right after that promise object is resolved.
I do not know how this is done in actual promises libraries, but I was able to re-create this functionality in the following way:
1) each promise has a waitingPromises property;
2) then method returns a new promise, and the original promise's waitingPromises property points to the new promise.
In this way, the chain of .then()s creates a structure that is similar to a linked list or rather a tree (each promise can have several waiting promises). A promise can be resolved only after its 'parent' promise has been resolved. The .then method itself is executed immediately, but the corresponding promise that it creates is resolved only later.
I am not sure this is a good explanation and would love to learn about other possible approaches.
Normally code is synchronous - one statement executes like (fileopen) and there is a guarantee that the next statement will execute immediately afterwards like filewrite()
but in asynchronous operations like nodejs, you should assume that
you have no idea when the operation will complete.
You can't even assume that just because you send out one request first, and another request second, that they will return in that order
Callbacks are the standard way of handling asynchrnous code in JavaScript
but promises are the best way to handle asynchronous code.
This is because callbacks make error handling difficult, and lead to ugly nested code.
which user and programmer not readble easily so promises is the way
You can think of Promise as a wrapper on some background task. It takes in a function which needs to be executed in the background.
The most appropriate place to use a promise is where some code is dependent on some background processing and it needs to know the status of the background task which was executed. For that, the background task itself accepts two callback resolve and reject in order to convey its status to the code which is dependent on it. In layman terms, this code is the one behind it in the promise chain.
When a background task invokes resolve callback with some parameter. it's marking the background operation successful and passing the result of the background operation to the next then block which will be executed next. and if it calls reject, marking it as unsuccessful then the first catch block will be executed.
In your custom promise, you can pass an error obj to the reject callback so that next catch block is aware of the error happened in the background task.
My main EXTJS function is calling three different AJAX asynch functions.The good news is that each AJAX function is successfully returning a boolean callback on whether the AJAX GET call was successful or not.
The problem - I need to perform additional post business logic from my main calling function once the boolean results have been returned from all three AJAX functions.
But as it is async the code will not wait for the async functions to complete and will fly through.
Any ideas on how I can get my code to GRACEFULLY wait until the typeof status is no longer 'undefined' ?
I am not near my code right now but here is a rough approximation.
function main(){
var status1, status2, status3, result1, result2,result3;
thisIsAsynchFunction1(args,function(result1){
status1 = result1;
});
thisIsAsynchFunction2(args,function(result1){
status2 = result1;
});
thisIsAsynchFunction1(args,function(result1){
status3 = result1;
});
// Perform logic based on callback from functions above
}
Thank You.
You cannot make Javascript wait. It simply doesn't work that way. What you can do is to trigger your final action when you discover that all three async results are done. Without redesigning your code to use promises (which would be the modern way to do this), you could do something like this:
function main(callbackFn){
var status1, status2, status3, doneCnt = 0;
thisIsAsynchFunction1(args,function(result1){
status1 = result1;
++doneCnt;
checkDone();
});
thisIsAsynchFunction2(args,function(result1){
status2 = result1;
++doneCnt;
checkDone();
});
thisIsAsynchFunction1(args,function(result1){
status3 = result1;
++doneCnt;
checkDone();
});
// Perform logic based on callback from functions above
function checkDone() {
// if all three results are done, then call the callback
if (doneCnt === 3) {
callbackFn(status1, status2, status3);
}
}
}
// call main and pass it a callback function
main(function(s1, s2, s3) {
// all three async results are available here
});
// Be careful because code here will execute BEFORE the async results are done
// any code that must wait for the async results must be in the above callback
A newer type of approach would be to have each async operation return a promise and when each async operation completes it would resolve its promise with its result. Then, you could use a promise library function (often called .when() to trigger a callback when all three promises were done. Many modern day JS libraries already return promises from ajax calls (such as jQuery) so the promise is already there.
I don't know ExtJS myself, but looked in its documentation and didn't see promise support yet and I found one thread discussing the fact that promises were not going to be in version 5. So, perhaps you just go with the counter design above or maybe ExtJS has some of its own support for monitoring when multiple async operations are all done.
I have code that requires waiting on a variable number of server responses along with loading several javascript modules via requireJS.
I want to leverage jQuery's when.apply. Unfortunately, the .then() portion of my code is always executing before ANY of my callbacks do.
Here is a summary of my code:
// load module func
function loadRequireJsModule(arg, callback) {
require([arg], callback);
}
// get layer info from server func
function getLayerInfoFromServer(arg, callback) {
var layerInfoListener = new DataLayer([arg]);
layerInfoListener.addRetrievedCallback(callback);
layerInfoListener.retrieveLayerInfo();
}
tasks.push( $.Deferred( function() {
loadRequireJsModule( "./" + layerJSON.type, function( module ) {
layer.controller = module;
});
}));
for (i=0; i<layerJSON.views.length; i++) {
layer.views[i] = {};
// get renderer class from require.js
tasks.push( $.Deferred( function() {
loadRequireJsModule( "./impl/" + layerJSON.views[i].renderer, function( index ){
return function( module ) {
layer.views[index].renderer = new module();
}
}(i));
}));
// POST request for layerInfo
tasks.push( $.Deferred( function() {
getLayerInfoFromServer( {
request: "configure",
layer: layerJSON.layer,
configuration: layerJSON.views[i]
}, function( index ){
return function( dataLayer, layerInfo ) {
layer.views[index].dataService = new TileService( layerInfo );
}
}(i));
}));
}
$.when.apply($, tasks).then( function() {
...
}
I'm worried that because my async functions are wrapped, jQuery is simply waiting until those functions end, which is immediate. I cannot pass jquery the naked async functions, they have to be wrapped. What do I have to do in order for my functions to be considered 'deferred objects'?
Edit - now that you've disclosed the actual code:
You are not using $.Deferred() correctly.
A typical usage in your context would look like this:
var d;
var tasks = [];
for (var i = 0; i < len; i++) {
// create new deferred object
d = $.Deferred();
// put deferred into tasks object
tasks.push(d);
loadRequireJsModule( "./impl/" + layerJSON.views[i].renderer, function( index, deferred ){
return function( module ) {
layer.views[index].renderer = new module();
// now that this operation is done, resolve our deferred
deferred.resolve();
}
}(i, d));
}
$.when.apply($, tasks).done(function() {
// code executes here when all the deferreds
// have had `.resolve()` called on them
});
Then, when all deferred objects that were put into the tasks array get resolved (by you calling .resolve()), then $.when() will fire.
You do not generally pass a callback to $.Deferred() like you were doing (that's used for something else and only used in special circumstances where you want to modify the deferred object in certain ways).
You MUST resolve or reject each deferred yourself by calling one of the methods on the deferred object that resolves or rejects it (there are four different methods that can be used).
Also, note that this structure runs ALL the async tasks in parallel (e.g. it fires them all at the beginning and then notifies you at the end when all of them have finished). You would need a different structure if you want them to run sequentially where you don't start the second one until the first has finished and so on.
If you want async behavior, then $.when() expects its arguments to be jQuery deferred or promise objects. So, for the structure of your code to work, the return from myAsyncWrapper() must be a jQuery deferred or promise object. That way, $.when() can monitor the promise objects for completion and when they are all complete, call your .then() handler.
Then, your async operations must either resolve or reject every one of the deferred objects that you passed to $.when() because it will only call its own .done() when ALL the deferred objects you passed to it have been resolved. Or, it will call its own .fail() if any of the deferred objects you passed to it is rejected.
If you are sure all the arguments you are passing to $.when() are deferreds or promises and your .then() handler is still getting called immediately, then your deferreds must already be resolved somehow because that's the likely explanation for why your .then() handler is called immediately.
If none of the arguments passed to $.when() are deferred or promise objects, then it will resolve itself immediately and thus call your .then() handler immediately which sounds like the behavior you are experiencing.
If your async operations are ajax calls, then jQuery.ajax() already returns a promise object that will be resolved when the ajax call completes. You can directly add the return result from jQuery.ajax() to your tasks array and pass that to $.when() and it will support your asynchronous behavior.
Using jQuery.ajax(), here's the concept for using $.when() with multiple concurrent ajax calls:
var promises = [];
for (var i = 0; i < myNum; i++) {
promises.push($.ajax(...));
}
$.when.apply($, promises).done(function() {
// put code here that runs when all the ajax calls have completed
// the results of all the ajax calls are in
// arguments[0], arguments[1], etc...
}).notify(function() {
// .notify is purely optional and only necessary if you want to be notified as
// each async operation completes
// This will get called as each async operation completes with whatever arguments
// were passed to the success handler
}).fail(function() {
// this will get called immediately if any of the async operations failed
// the arguments are the same as the .done() function except that some may be empty
// if they didn't complete before one of them failed
});
Obviously, you don't have to use all three operations .done(), .notify() and .fail() or you can specify all of them in .then() - I just included all three with comments for educational purposes.
If you want help with some other type of async operation that doesn't already create its own promise object, then I'd be happy to help if you show and describe the actual operation. Deferreds are confusing to get a handle on initially, but like many things are actually quite simple and extremely useful once it finally sinks in.
There is an alternative I found for processing a loop of parallel promises without using an array. Instead use the pattern promise = $.when(promise, anotherPromise).
e.g. for you example something like this
// Start with an empty resolved promise - undefined does the same thing!
var promise;
for (i = 0; i < layerJSON.views.length; i++) {
layer.views[i] = {};
// get renderer class from require.js
promise = $.when(promise, loadRequireJsModule("./impl/" + layerJSON.views[i].renderer, function (index) {
return function (module) {
layer.views[index].renderer = new module();
}
}
));
}
// Now do something after all the promises are resolved
promise.then(function () {
// Do the next part
});
This example assumes you change getLayerInfoFromServer to return a promise:
Notes:
The downside of this technique is that the return values of the individual promises are nested within each other, in reverse order, so are not very useful. It is great if you just need to know when it has completed all tasks.
I have a set of async functions that issue executeSql commands to drop 2 tables, and have callbacks to create the 2 tables and populate the 2 tables.
I'd like to know when they have ALL completed.
I wouldn't mind if they executed synchronously. In fact, I'd prefer that they ran synchronously!
Q: Would I use the jQuery pipe method to queue up these functions so that they execute in a more traditional way than issuing callbacks?
I want to do something like:
DropTableA();
CreateTableA();
PopulateTableA();
DropTableB();
CreateTableB();
PopulateTableB();
window.location.replace('Index.htm');
Have a look at jQuery's Deferreds and Promises.
In short, here's an example of an async function that executes something asynchronously (setTimeout in this case). When calling the function you get back a promise.
var myFunc = function (value) {
var d = $.Deferred();
setTimeout(function () {
d.resolve(42 * value);
}, 1000);
return d.promise();
};
var promise = myFunc(100);
promise.done(function (res) { console.log(res); });
You can use $.when to execute something when all promieses have been fulfilled, i.e.
$.when(p1, p2, p3).then(...)
If you want to execute them in order you can chain the deferreds using the deffered's pipe.
If they are all sinchronous function (no async call inside those function) you are assured that window.location.replace('Index.htm'); is called after all of them. If you make ajax call you could do
jQuery.ajaxSetup( {async: false});
befare calling the first function and you are ok because all your AJAX call are now synchronous
http://jsfiddle.net/ywL63/
This is where jQuery deferred/promise come into play. Basically you modify your functions like this
function myAction1() {
var dfd = $.Deferred();
$(selector).whatever(function() { dfd.resolve(); // this is one of the callbacks});
return dfd.promise();
}
Then you can go and "wait" for all of the actions to complete with a construct like
$.when(myAction1(), myAction2(), ...).then(function() {
// this will be executed when all the actions finished = resolved
}
If your functions are synchronous, just call them in the right order, as you did in your example.
Assuming instead that your function returns a promise() or an observable object you can simply do
$.when(fn1(), fn2(), ... ).done(function() {
/* here you know that all functions have been returned, as you asked */
})
(note that you can use deferred objects not only for asynchronous tasks)
Edit: if order matters you can use this plugin:
jQuery.whenSync() Plugin For Chaining Asynchronous Callbacks Using Deferred Objects