I'm having some issues with the chaining of deferred objects. So I think that Im missing some understanding. My code is something like follow:
var AJAX_FUNC_CREATE_ALIAS = function(){
return $.when(ajax_call()).then(function(response){
// DO something with the response I get to compose an object
return composed_response;
});
}
var name = 'Alejandro',
alias = 'Ali';
$.when(AJAX_FUNC_CREATE_NAME)).then(function(response, status, jqXHR){
return AJAX_FUNC_CREATE_ALIAS(name); // <-- Wait correctly
},function(jqXHR, status, errorThrown){
return default_response;
}).done(function(artist_response){
var promise = AJAX_FUNC_CREATE_ALIAS(alias); // <----- Problematic one
console.log(promise.state()); // It shows pending state
return promise;
}).done(function(alias_response){
$.publish(channel, [alias_response])
});
The execution goes like this:
The AJAX_FUNC_CREATE_NAME function start execution. When it finishes it goes to the callback defined inside the then().
It executes the AJAX_FUNC_CREATE_ALIAS(name) function. The .done() method is not executed until the AJAX_FUNC_CREATE_ALIAS(name) has finished.
It start executing AJAX_FUNC_CREATE_ALIAS(alias); In here it does not wait to get answer from the server. It goes straight to the $.publish(....)
Why?
Update: I have added some code for checking the answer I get from the problematic line. The promise I get back seems to have 'pending state'. From the jquery documentation .done() methods : "Add handlers to be called when the Deferred object is resolved." [http://api.jquery.com/deferred.done/] . If the state is pending... why is the done() method getting it?
then returns a new promise,done doesnt.
If you want to chain tasks use then.that's what you did for the second promise,it only makes sense to do the same for the next one.
It's written right here:
http://api.jquery.com/deferred.then
Related
I was going through some tutorial materials and I notice that in Mongoose, I could somehow defer the final execution of a promise in it but I wonder how that is done.
For example, I can call the find function and it returns the promise of the result likes so:
const blogs = await Blog.find({id : '123'});
The find() function in mongoose calls the exec() function in the Query object to complete the query and return the result, like in the line of this file.
Then, let's say that I have the following modifications to the prototype of the Mongoose Query object to see whether I should retrieve data from cache or from Mongo:
mongoose.Query.prototype.cache = function() {
this.useCache = true;
return this;
}
mongoose.Query.prototype.exec = async function() {
if (!this.useCache) { // <-- Apparently, I don't understand how this.useCache can be true if this.cache() was called
this.exec.apply(this, arguments);
}
return 'some cached value';
return this;
}
// Somehow, the find() section of the chaining is capable of waiting for cache() which is called later to complete to know that useCache is true! But how is that done?
const blogs = await Blog.find({id : '123'}).cache(); // <-- how did this mange to return 'some cached value'?
However, since exec() is already called and evaluated in find() which is executed before the cache() function in the chain, how could this.useCache still be eventually evaluated in the exec() function finally when it resolves?
Unless there are some ways to wait until everything else down the chain have finished executing, in this case find() waited for cache() to finish executing, I would have expected this.useCache to always be undefined, wouldn't it?
I thought it's pretty amazing and would actually like to know how to implement a similar kind of chaining that is capable of somehow deferring the final action until all the functions in the later part of the chain to complete before resolving a result.
Note:
The above examples are my simplified version of the actual code for readability sake. The links to their actual files can be seen here and here.
The find() function in mongoose calls the exec() function in the Query object to complete the query and return the result, like in the line of this file.
Actually no, it doesn't, at least not in your case. See the comment from three lines above:
// if we don't have a callback, then just return the query object
The .exec() method is only called by find(…, callback) if you pass a callback. When using promises, you don't.
Instead, the exec method is called by the then() method of the query which makes the query thenable, and which gets used when you await the query object.
-- EDIT --
I encountered a weird thing recently about promises, but I guess it's maybe because it's against the philosophy of promises.
Considering the following code :
// Assuming Auth is just a simple lib doing http requests with promises
Auth.signup()
.then(succCall, errCall)
.then(loginSucc, loginErr)
// My callbacks here
function succCall (){
// OK, send second promise
console.log('succCall');
return Auth.login();
}
function errCall(){
// I do some things here and now
// I want to break out from here
console.log('errCall');
}
function loginSucc(){
// This is the callback of the login method when it went OK
// I want to enter here ONLY if with go through the succCall
console.log('loginSucc');
}
function loginErr(){
// This is the callback of the login method when it went not ok
// I want to enter here ONLY if with go through the succCall
console.log('loginErr');
}
Here if something goes wrong in Auth.signup(), this is what show :
errCall, loginSucc
if i do a $q.reject() in the errCall this is what happens :
errCall, loginErr
and this is what i want :
errCall... finish, stop here
Now, the problem is, it goes in errCall when signup goes wrong, that's good, but then it enters loginSucc...
I want to break out of the then chain when any errorCallback (which is errCall or loginErr here) is encountered.
-- EDIT --
I think i was misunderstood by some mean, i want to totally break the chain without check in any other "then" if something went wrong.
As if i was saying : if first then is wrong stop here, if first then ok continue, if second "then" ok continue, if third "then" wrong, stop
// Just like if i did the following but by chainning "then" methods
// My callbacks here
function succCall (){
// OK, send second promise
return Auth.login().then(loginSucc, loginErr);
}
My point is, i don't want only one error handler if i have many "then" chained
What is effectively happening is this:
try {
try {
var a = succCall();
} catch(e1) {
a = errCall(e1);
}
var b = loginSucc(a);
} catch(e2) {
b = loginErr(e2);
}
You can break out of the chain by calling
return $q.reject('Reason Err was called');
in your errCall() function.
EDIT:
As OP remarked by calling $q.reject the code will enter the loginErr function.
Alternatively you can modify your code like this:
Auth.signup()
.then(function(a) {
succCall()
return loginSucc(a).then(null, loginErr);
}, errCall)
You can read more in these two SO question:
Break promise chain
Break Out of then promises in
Angularjs
This also is a helpful read : Flattening Promise Chains
errCall function needs tor return a promise, and that promise needs to be rejected for loginErr to be fired.
function errCall(){
// i do some things here and now
return $q(function(resolve, reject) {
// auto reject
reject();
});
}
Alternatively try .catch:
Auth.signup()
.then(succCall)
.then(loginSucc)
.catch(function(err){
// caught error, problem is you won't know which function errored out, so you'll need to look at the error response
});
Just don't pass any errCall or loginErr to then and use catch() in the end of the promise chain and it will be interrupted on first error, which will be passed to catch(). If you want explicitly process error of Auth.signup() then your errCall should look like this:
function (err) {
if(isFatal(err)) {
return Promise.reject(new Error('Fatal!')); //`catch()` handler will be called with `new Error('Fatal!')`
} else {
return 'something'; //next `then()` handler will be called with 'something'
}
}
Your best option is to return a promise that is never resolved from errCall():
function errCall() {
console.log('errCall');
return $q.defer().promise;
}
But you are right, what you are trying to do is "against the philosophy of promises".
Why you shouldn't do it
It is of course a problem that your loginSucc function is called when an error occurs during evaluation of the first promise. However, that can be fixed by rejecting once again in errCall as others have already pointed out. But you should not try to avoid the execution of loginErr, otherwise something is conceptually wrong with your code.
The evaluation of the expression
Auth.signup().then(succCall, errCall)
yields another promise. The contract is that a promise has a then() method taking two callbacks as parameters, one for success and one for failure. The contract is also that the success callback is called when the promise is evaluated successfully and that the error/failure callback is called in all other cases. If you intend to never call either of those, don't use a promise!
Tools: JavaScript ES6
I haven't seen a good succinct answer about the syntax of chaining multiple promises to execute in order. I thought this would be a good nail in the coffin question for all promise newbies out there. :)
My Issue is that I want to call this in a synchronous order getPosts--->getThreads--->initializeComplete()
Here is what I am doing.
userPromise.then(getPostsPromise).then(getThreadsPromise).then(initializeComplete());
userPromise is Promise obj I returned from another part of the code
getPostsPromise returns a Promise and makes a fetch to the server for posts
getThreadsPromise returns a Promise and makes a fetch to the server for threads
initializeComplete is a callback to tell my program that it is initialized.
Here is an example of one of the promises in the chain:
var getPostsPromise = function(){
//Firebase is just a simple server I'm using
var firebasePostsRef = new Firebase("https://myfburl.firebaseio.com/posts");
var postsRef = firebasePostsRef.child(localPlace.key);
return new Promise(function(resolve, reject) {
//Below is a Firebase listener that is called when data is returned
postsRef.once('value', function(snap,prevChild) {
var posts = snap.val();
AnotherFile.receiveAllPosts(posts);
resolve(posts);
});
});
}
But initializeComplete() is being called before getPostsPromise and getThreadsPromise have a chance to finish fetching.
Why is that happening and how do I write the promises to execute in order?
initializeComplete is getting called right away because you are invoking it when passing it to then. You have to omit the parentheses, just like you did for getPostsPromise and getThreadsPromise
userPromise.then(getPostsPromise).then(getThreadsPromise).then(initializeComplete);
While yts's answer is correct (the issue is you're invoking initializeComplete instead of passing the function), I'd rather format the calls a bit differently. Having each callback function call the next function is a bit against the design of promises. I'd rather each function return a promise, and then call then on the returned promise:
userPromise
.then(function(){
return getPostsPromise()
}).then(function(){
return getThreadsPromise()
}).then(function(){
return initializeComplete();
});
or to pass the actual returned objects and not have to do any additional intermediate processing:
userPromise
.then(getPostsPromise)
.then(getThreadsPromise)
.then(initializeComplete);
currently I am reading these two answers to better understand the problem and get better at javascript:
wait for async task to finish
Promises in AngularJS and where to use them?
But at the moment I have this code:
function getRegistration(id) {
var numRegistered = 0;
api.search({num: id, state: "done"})
.then(function(response) {
numRegistered = response.data.content.length;
)};
console.log(numRegistered);
}
now I can expect numRegistered to be 0 because it probably executes that statement before the asynchronus call has finished. I am finding it hard to understand how to do this so that I wait for the call, assign the value and return it...the solutions appear to be use a call back function or use a promise. Could someone help me (yes I come from an object oriented background...).
api.search basically executes an $http.get.
Here is the promise approach:
function getRegistration(id) {
return api.search({num: id, state: "done"}); // we just return the promise
}
And then, in your controller or a service, you'd wait for it to resolve:
getRegistration(id).then(function(res) {
var numRegistered = res;
// rest of the code here
});
But again, although now your function returns something (a promise), you still need to wait for the promise to be resolved before having numRegistered available.
This is very similar to what happens inside your original .then callback, but here we've moved the non-getRegistration code outside of the getRegistration function, assuming that getRegistration is inside some service that shouldn't know about the rest of your code.
Here is my basic situation:
function somePostThing() {
return $post("/someUrl").done(doSomething);
}
function doSomething(data) {
// do stuff with the data
}
var object = {};
object.deferred = somePostThing();
// A few cycles later, object.deferred may be resolved or unresolved
object.deferred.done(function () { /* ... */ });
The last line may or may not work, because done won't fire in the event that the deferred object is already resolved. I would like to be able to do something like this:
function doSomethingWithData(data) {
// do stuff
}
var value;
if (object.deferred.isResolved()) doSomethingWithData(object.deferred.value());
else object.deferred.done(doSomethingWithData);
How do I get the value of an already resolved jQuery.Deferred()?
No, that's actually exactly why the whole "Deferred" mechanism came into being. If you pass in a "done" function after the asynchronous process has been resolved, it most definitely will be executed immediately.
From the jQuery API docs:
If more functions are added by deferred.then() after the Deferred is resolved, they are called immediately with the arguments previously provided.
That's true for the ".done()" functions also.
JavaScript in a browser is single threaded. So, in the following code snippet:
object.deferred = somePostThing();
// Nothing can happen to object.deferred here
object.deferred.done(function () { /* ... */ });
nothing will happen in between the first line and the last line. "A few cycles later" doesn't mean anything in JavaScript-land. Something will only happen to object.deferred after the function that is executing returns.