Javascript promise/then not executing in correct order - javascript

Here's the code:
vm.saveData = function(data) {
demoService.saveData(data, function(response) {
if (response.status === 200) {
principal.identity(true).then(function() {
$state.reload();
return toastr.success('Success');
});
}
return toastr.error('Failure');
});
}
On getting success response from the api, it should display only the 'success' message. But, instead it displays the 'failure' message first and then the 'success' message. What am I doing wrong? Do I have to put a timeout or is there something which I'm missing here?

If the status is 200 then you set up a promise to call success later on.
Regardless of what the status is (because it is outside of the if and you haven't used an else) you always call error.
Presumably you just want to move return toastr.error('Failure'); into an else

That's not how you setup promises. Promises uses .then(). You are simply using passing the function in as a callback.
vm.saveData = function(data) {
demoService
.saveData(data)
.then(success, error);
function success(response) {
principal.identity(true).then(function() {
$state.reload();
return toastr.success('Success');
});
}
function error(response) {
return toastr.error('Failure');
}
};

Many systems such as AJAX send multiple messages to indicate progress in the task. You want to ignore the earlier messages. The failure message is from the earlier events while the action is incomplete.

I found out my mistake. Adding 'return' resolved the issue.
'return principal.identity(true).then(function(){
//do something here
});'
vm.saveData = function(data) {
demoService.saveData(data, function(response) {
if (response.status === 200) {
return principal.identity(true).then(function() {
$state.reload();
return toastr.success('Success');
});
}
return toastr.error('Failure');
});
}

Related

How to make synchronous call in Angular JS?

The following code supposed to be update username in the data base then retrieve updated username.
updateUserMame and getUserName are two different REST calls.
updateName(name) {
var obj = this;
if (name === 'None') {
name = null;
}
obj.UtilityService.updateUserName(name, obj.userId)
.success(function (data) {
if (data) {
obj.getUserName(obj.userId);
console.log('Name is updated for ID:'||obj.userId);
} else {
console.log('Something Wrong');
}
});
}
getUserName(userId){
obj.UtilityService.getUserName(userId)
.then(function (result) {
console.log(result.user.userId);
}
}
I have user name 'Nathan Drake' in the dataBase.
When I run the update function with 'Elena Fisher', it is returning 'Nathan Drake'.
I've read some articles to make synchronus service calls, but unable to figure out what is going wrong.
Please help.
You could wrap your update function in a promise:
var updatePromise = $q.when(updateName(name)); // creates a promise
When your promise has finished processing, you can resolve it using then() which takes a success callback and an error callback
updatePromise().then(function successCallback(response){ // resolves the promise using then
getUserName(userId) // execute the rest of your code
},
function errorCallback(response){
console.log(error)
});
You would need to inject $q into the scope you are working with
Your code does not make much sense, that is I see possible mistakes as it looks like you are interchanging user name and user id and calling the obj context from inside a function even when its not declared there etc. Either we are missing code or this will fail when you try to run it.
Here is your example with some fixes and comments that show how you could do it using callbacks (no sync code, as mentioned by everyone else on this thread you should avoid actually waiting for I/O and use callbacks instead).
updateName(name) {
var obj = this; // good, you captured this
if (name === 'None') {
name = null;
}
obj.UtilityService.updateUserName(name, obj.userId)
.success(function (data) {
if (data) {
// ok, you successfully updated the name so why would you go back to the server and get it again? You know the value based on your update.
console.log('Name is updated for ID:' + obj.userId.toString());
// for your example though here is how you could handle it
obj.getUserName(obj, obj.userId, function(user){ // i assumed the name is stored in variable userName
console.log('Name from server = ' + user.userName); // no idea what you are returning but you can figure it out from here
// maybe you also want to capture it again??
obj.name = user.userName;
});
} else {
console.log('Something Wrong');
}
});
}
// pass in captured this as obj, the user id, and a callback
getUserName(obj, userId, callback){
obj.UtilityService.getUserName(userId)
.then(function (result) {
callback(result); // call the callback with the result. The caller can then do something with it
}
}

Return false doesn't stop the code in ajax $.post

I execute an ajax request using $.post, this is my code:
$.post(postUrl, postData, function(response)
{
if(response.status == "SUCCESS")
{
updateConfirmFrame();
}
else
{
return false;
}
}, 'json');
now if the response return SUCCESS the code continue calling a function that unlock some control, but if an exception is handled, so the code should blocked. Now I've put an alert in return false; condition and the alert is printed correctly but, the return false doesn't stop the code. I don't want that the code continue the execution. I also tried with throw Exception but doesn't working. What am I doing wrong?
UPDATE
$.post(postUrl, postData)
.done(function(response)
{
if(response.status == "SUCCESS")
{
updateConfirmFrame();
}
else
{
alert("error"); //The problem is here
return false;
}
})
.fail(function(err)
{
return false;
});
alert("hi wor")
Use that:
$.post(url, postdata)
.done(function(response) {
})
.fail(function(err) {
// error
});
The syntax you used is $.post(postUrl, postData, success_callback, fail_callback);
return statements only return the function they are within, stopping further code execution in that function. They do not stop JS code execution in general.
The alert statement you are saying shouldn't run because you've done return false; is not in the function returning false. It would not be affected by the fact that some other function returned false. It absolutely should run.
On top of that, the success function for your post call is an event callback. It is created, but does not run until the actual loading of the file happens, which is gonna be long after other code outside your ajax stuff finishes. So the code in that function isn't even gonna be executing before that alert takes place.

The variable scope/nesting of AngularJS variables

I have this angular controller:
app.controller('FeedCtrl', function ($scope, Profile) {
$scope.getID = function() {
Profile.getUID()
.then(function (data) {
if (data !== null) {
console.log(data.id); // returns correct id
$scope.data = data;
} else {
console.log("Could not retrieve id");
}
}, function (error) {
console.log(error);
});
console.log($scope.data); // logs: undefined
return $scope.data; // logs: undefined
};
var somedata = $scope.getID();
console.log(somedata); //just returns undefined
});
And this Factory that the controller uses for a JSON request.
module.factory('Profile', function($http, $localStorage) {
return {
getUID:function(){
return $http.get("https://graph.facebook.com/v2.2/me", {params: { access_token: $localStorage.accessToken, fields: "id,name,gender,location,website,picture,relationship_status", format: "json" }})
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return $q.reject(response.data);
}
}, function(response) {
// something went wrong
return $q.reject(response.data);
});
}
};
});
The Question
I am unable to change the value of $scope.data for use outside the $scope.getID function but inside the rest of the FeedCtrl.
If you look on the comments you see what I am getting returned in the console logs.
I've tried to understand this problem by searching here in StackOverflow and Google but it seems that I don't understand the $scope concept of AngularJS.
I am grateful for any push in the right direction.
That's a classic mistake, the code you're calling is asynchronous, look at your console and watch the order of your logs, the log that will return the correct id will be the last one because it will be called only after the promise has been resolved.
Is this enough of a push in the right direction for you?
A very simple example, but it's the same principle.
setTimeout(function(){
document.write('A2: Timeout is done');
}, 5000);
document.write('A1: Called timeout, this gets logged before A2 even though my line number is higher.');
That is not a scope problem, it's a time problem. You are trying to use the value before it exists.
In the controller you can only use the value after the result has arrived, i.e. in the callback for the then method. The return statement runs before there is a result, so you can't return it.
Once you have made an asynchronous call, the result has to be handled asynchronously. You can return a Future object to handle the result when it arrives, but you can never make a function that makes an asynchronous call and returns the result itself.
Your code is asynchronous. I wrote a response to this exact same problem in this post.
Returning after ajax call prints false

How to continue executing JS after deferred.fail()

Here is some code that takes a Backbone model, saves it and then waits for the response and fires the jQuery .done() or .fail() code. This is working fine but on fail we actually want to get the returned message from the service add it to our errors object. This is all within a Backbone validate() function; after this code, we check the errors object and display the message if there is one.
It seems like if we .fail(), everything stops executing. We need to continue the validate function. I found this question/answer but it didn't seem to make a difference: Is there a way to continue after one deferred fails?
Any way to continue executing code after hitting a deferred.fail()?
addressModel.save().done(function() {
console.log("promise done");
model.set("...", false);
}).fail(function(response) {
console.log("promise fail");
if (response.responseJSON && response.responseJSON._messages) {
_.each(response.responseJSON._messages, function(value, key) {
errors[key] = value[0];
});
}
});
It's possible but tricky - at least until 3.0. The trick is:
Don't use .fail use .then.
Return a resolved deferred from the fail.
This is like signalling that we've dealt with the exception we got here:
var p = addressModel.save().then(function() {
console.log("promise done");
model.set("...", false);
}, function(response) {
console.log("promise fail");
if (response.responseJSON && response.responseJSON._messages) {
_.each(response.responseJSON._messages, function(value, key) {
errors[key] = value[0];
});
}
return $.Deferred().resolve(); // return a resolved deferred
});
This will let you do:
p.then(function(){
// this code runs when either failure or success happened, can also chain
});
We never could get this to work with a promise, whether it was returned by a function within Backbone's validate() or by validate() itself. But I found a solution within Backbone itself--save() will accept async: false and stall execution until a response is received, then continue from there. It probably uses a promise to do this behind the scenes.
addressModel.save(null, {
async: false,
success: function() {
model.set("...", false);
},
error: function(model, response) {
if (response.responseJSON && response.responseJSON._messages) {
_.each(response.responseJSON._messages, function(value, key) {
errors[key] = value[0];
});
}
}
});

Angular Response Interceptor--Handling Promise

I have created the interceptor below--it basically redirects to a known location when the service sends a response indicating that the user's session has expired.
It currently works correctly--what I'm not sure about is whether to reject the promise, return the response, or do something different. If I just redirect, it works as expected. If I reject the promise, I end up in the error handler of the ajax call (not shown), but it otherwise successfully redirects.
What is the correct way to fulfill the promise in this scenario?
Edit: Added the else clause.
var responseInterceptor = function ($q) {
return function (promise) {
return promise.then(function (response) {
if (response.data.SessionHasExpired == true) {
window.location.href = "/Home/Login?message=User session has expired, please re-login.";
return $q.reject(response); //do I need this? What to do here?
}
else {
return response;
}
}, function (response) {
return $q.reject(response);
});
};
};
In such a case, I think you should handle the error inside the error callback, and only reject the promise if the error isn't something you were expecting. I mean, deal with the session timeout error and reject the promise for everything else. If you reject it even after handling the error, all errors callbacks related to the promise will be invoked (as you've noticed yourself) and none of them will handle the session timeout error (after all, you have made an interceptor for doing precisely that).
I support the #idbehold advice of using a more appropriate status code for this situation. 401 Unauthorized is the way to go.
With all of this being considered, your code could look like this:
var responseInterceptor = function($q) {
return function(promise) {
var success = function(response) {
return response;
};
var error = function(response) {
if (response.status === 401) {
window.location.href = "/Home/Login?message=User session has expired, please re-login.";
}
else {
return $q.reject(response);
}
};
return promise.then(success, error);
};
};
Perhaps you'd be interested in checking out this Angular module on Github.

Categories

Resources