how to break multiple Ajax promise chain? - javascript

I have multiple ajax request working together, and each request based on previous request's result, if the previous request return false, the chain should stops.
Here's some code
//here is a promise chain
return this.getBand(id)
.then(this.getAlbum)
.then(this.getSong);
//ajax request for getBand
function getBand(id) {
return Ember.$.ajax({
data:{id: id},
url: urls.bandUrl,
}).then(function(result){
return result;
});
};
//ajax request for getAlbum
function getAlbum(result){
if(result.pass) {
var bandName = result.name;
return Ember.$.ajax({
//...
})
} else {
// i wanna stop the promise chain here, how to do that?
}
}

You can indicate an error in the chain by returning a rejected Deferred:
function getAlbum(result) {
if (result.pass) {
// ...
} else {
return Ember.$.Deferred().reject('Previous result did not pass');
}
}
You can also revise getBand() to check result.pass itself, so getAlbum() won't be invoked unless it did pass.
function getBand(id) {
return Ember.$.ajax({
// ...
}).then(function(result){
return result.pass ?
result :
Ember.$.Deferred().reject('Band could not be found (' + id + ').');
});
};
The chain won't completely stop, but it will only proceed to fail callbacks/filters, provided to .then() as a 2nd argument or .fail().
return this.getBand(id)
.then(this.getAlbum)
.then(this.getSong)
.fail(function (error) {
// show `error` to user
});

Related

My asynchronous javascript gets executed in the middle of the other function

Im trying to execute a function after the other one in Vue.js. I've already tried async/await, callback functions, .then, but it somehow doesnt want to load one after the other. What is a possible solution?
auth_mixin.js:
async auth () {
console.log("authban")
var token = this.getCookie("token")
var jsonData = {}
jsonData["token"] = token
console.log(jsonData)
var bodyFormData = new FormData();
bodyFormData.append('data', JSON.stringify(jsonData));
axios({
method: 'post',
url: 'backend/index.php?action=checkAuth',
data: bodyFormData,
headers: {'Content-Type': 'multipart/form-data'}
})
.then(function (response) {
console.log(response);
if(response.data.status==="OK"){
console.log("ok")
return true;
}else{
console.log("nem ok")
return false;
}
})
.catch(function (response) {
console.log(response);
return false;
});
}
Navbar.vue:
created () {
var result=false
this.auth().then(this.checkIfLoggedIn(result))
},
methods: {
checkIfLoggedIn (isLoggedIn) {
console.log("na ez lesz az erdekes "+isLoggedIn)
if(isLoggedIn === true){
console.log("true")
document.getElementById("logged_out").style.display="none";
document.getElementById("logged_in").style.display="block";
}else{
console.log("fail");
}
}
}
this.auth().then(this.checkIfLoggedIn(result))
You have two problems.
First: this.checkIfLoggedIn(result) calls checkIfLoggedIn immediately. You need to pass a function to then.
this.auth().then(() => this.checkIfLoggedIn(result))
Second: With that change, you call checkIfLoggedIn when auth resolves.
So when does auth resolve? Well, it is defined with the async keyword, so it resolves when it returns (unless it returns a promise, in which case it adopts that promise instead).
So what does it return? It has no return statement, so it returns undefined when it gets to the end … which is immediately after the call to axios (since you aren't awaiting that).
If you returned the return value of axios(...).etc then it wouldn't resolve until that promise resolved.
(Aside: You're using async, you should probably refactor to use await, try {} catch() {} instead of .then() and .catch()).

Return an API response from a Promise.then() in AngularJS

I'm new in AngularJS and wanted to write a function in AngularJS, which tries to delete a user. On success it should return a boolean, which determines if he was succesfully deleted.
Anyway, the extracted result out of the promise always returns true, even if the Api-Response is false. I tried extracting the response as written here: Return value from a promise in Angular. I also tried other links, but none of them really helped me solving my problem.
user.controller.js:
deleteUser(user) {
const self = this;
self.userActionPromise = self.userService.deleteUser(user.id).then(deleted => {
if (deleted) {
self.messagesService.successMessage('user.DELETE_SUCCESS');
self.loadUsers();
} else {
self.messagesService.errorMessage('user.DELETE_FAILURE');
}
});
}
user.rest.service.js:
self.user = $resource(config.restServerUrl + 'users/:id', {id: '#id'}, {
delete: {method: 'DELETE'}
});
deleteUser(id) {
return this.user.delete({id: id}).$promise.then(deleted => {
return !!deleted;
});
}
result of
return this.user.delete({id: id}).$promise.then(deleted => {
return !!deleted;
})
always return true when request finished.
If you want to cover fail result:
return this.user.delete({id: id}).$promise.then(deleted => {
return deleted;
}, function (error) {
return error;
})
second function will be executed when server return an error
https://www.baeldung.com/spring-boot-custom-error-page
Http responce statusCode should say that came an error, like 500 (https://developer.yahoo.com/social/rest_api_guide/http-response-codes.html).
When you return status code 200, and result false, it just say to angularjs that request finished successfuly and result of it false

Wrap two promise in a function

I have some issue with the return of promise. Before, during an http call, I used a function like this returning one promise:
get_data: function (url)
{
let timestamp = new Date();
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}).then(
function successCallback(response)
{
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response)
{
console.dir(response);
return response;
});
},
It was quite straight forward and I could use it like this:
get_data('my_awesome_url').then(function(response){
let my_awesome_data = response
})
The culprit is the timestamp thingy. I use it for some authentification, the why is not important, but by getting it from the client side I was quite often victim of bad horloge or system local set in another langage.
My solution was to create a function that request a server timestamp . But by doing this I must first wait for the timestamp request to hand, then launch another request and... wait for it to end.
This is where I don't really know what to do. My code look like this:
get_data: function (url)
{
let timestamp = new Date();
get_timestamp().then(function(){
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}).then(
function successCallback(response)
{
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response)
{
console.dir(response);
return response;
});
});
},
But I'm not sure what I should return. Should I return the get_timestamp promise and in the "then" wait for the other request to end? Should I make the get_timestamp a synchronous call because after all it's just a little date string?
I used the old function all the way accross my code so a way to just keep the old use (with only one then) would be awesome.
As always thanks all.
You would write it that way:
get_data: function(url) {
return get_timestamp() // request the timestamp this returns a promise
.then(function(timestamp) { // on which then is called wich itself returns a promise.
// the callback of this then is called as soon
// as the promise returned by timestamp
// is resolved
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}) // here you return the Promise that is created by the $http
})
.then(function(response) { // the callback of this then is called as soon
// as the previous promise was resolved
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
})
.catch(function(response) {
console.dir(response);
return response;
});
},
First of all I would use:
.then(function(response) {
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
})
.catch(function(response) {
console.dir(response);
return response;
});
Instead of
.then(
function successCallback(response) {
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response) {
console.dir(response);
return response;
});
})
Because it is easier to read later if you have longer chains.
The return returns the last Promise that was created through the chain, the one that was returned by the call .catch(function(response) {...}
You should chain the Promises and return the result of the chain:
function get_data(url) {
return get_timestamp()
.then((timestamp) => {
return $http({
method: "GET",
url: url,
headers: {
timestamp: timestamp
}
});
})
.then((response) => {
console.dir("Response:");
console.dir(response["data"]);
return response["data"];
})
.catch((response) => {
console.dir(response);
return response;
});
}
Note that we only need one .catch at the end of the chain to catch all exceptions.

AngularJS: Exclusive error handling in $q execution chain

Having an angular service that returns promise, is it possible to detect whether a consumer of this promise handles error ? I'd like to provide a default error handling in service, but ensure that it would be used only if no error handler is defined down the execution chain.
The service method looks like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
console.log('ERROR!'); // default error handling
}
);
}
The serviceMethod returns a promise, therefore:
1) If the consumer provides error handler, the error should be handled exclusively by it.
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
// Custom error handling.
});
}
2) If the consumer doesn't provide handler, the error should be handled exclusively by service handler.
Is it possible to achieve in the first successor of serviceMethod? Is it possible at any point in the chain (the error is handled exclusively by the first consumer to provide error handler)?
You have the answer in the code you haven given. Do it like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
return response; // default error handling
}
);
}
And your getResponse method:
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
alert(error.code); //Default error handling returned from error function in serviceMethod
alert("My custom error"); //Custom error handling
});
}
It is very important that the rejection handler in the service throw the error response. Otherwise the $q service will convert the rejected promise to a successful response.
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (errorResponse) {
//return response; // default error handling
throw errorResponse;
//OR
//return $q.reject(errorResponse);
}
);
}
A common problem is erroneous conversion of rejected promises to fulfilled promises by failing to return anything. When a function omits a return statement, the function returns a value of undefined. In that case the $q service will convert a rejected promise to a fulfilled promise that resolves with a value of undefined.
That said. No, it is not possible for a service to know how a consumer will use a rejected promise. If a consumer wants a service to skip default error handling, the consumer must specify that in the service call:
function serviceMethod(method, url, data, skipErrorHandling)
{
return $http({
method: method,
url: url,
data: data
})
.then(function (response) {
return response.data;
})
.catch(function (errorResponse) {
if (skipErrorHandling)
throw errorResponse;
}
//Put error handler here
//Correct error
var promise = retry(method, url, data);
return promise;
);
}

Parse.com and closures

I have a Cloud Code function that will execute n times the same block. The block consist in an http call with auth header. to make things simple, I have created a function at the root of my main.js. The function needs to return a result and keep in memory the authData (in order to reuse it for future calls).
function requestURI (uri){
var authData; // gets generated if null, should be reused if not null
var result; // supposingly contains the results
return something();
}
The function something() is a Parse.Promise because I need my calls to be asynchronous. As I understand I can't attach the result nor the authData to my promise.... If I run a console.log() within the function requestURI(), I see authData and result correctly populated as expected
Then I want this function from a Parse function. (the whole purpose is to have the function being re-usable by any other)
Parse.Cloud.define("testCall", function(request, response) {
var uri1 = '...';
var uri2 = '...';
var uri3 = '...';
return requestURI(uri1).then(function(){
// how do I get the result of my request?
return request(uri2);
}).then(function(){
// how do I get the result of my request?
return request(uri3);
});
}
The problem I have is that I can't retrieve my result out of the requestURI function and it seems that authData is reset everytime I run the function
I read the solution lies in closures but I can't get my head around them...
edit: add the function something():
return Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
},
success: function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// I need to return the result object in a promise
result = httpResponse.data;
// return a Promise that can be handled by any function
return Parse.Promise.as(result)); // this promise doesn't work
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
return (null,Parse.Promise.error(httpResponse.text));
}
});
edit: here is what I'm trying
// authData is not null, we can make an authenticated call
function makeAuthenticatedRequest(){
// generate the appropriate auth Header;
var digestAuthHeader = generateDigestAuthHeader();
return Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
}}).then(function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// create the final object to return in a promise
result = httpResponse.data;
console.log(result) // returns something not null!!!
// return a Promise that can be handled by any function
return promise.resolve({'authData': authData, 'result': result});
},
function(error) {
console.error('Request failed with response code ' + error.status);
return (null,Parse.Promise.error(error));
});
}
Parse.Cloud.define("testCall", function(request, response) {
var uri1 = '...';
var authData;
return apiCall1001Menus(authData,uri1).then(function(result){
response.success(result); // returns {}
});
});
my response callback is {}!!! which is not what I would expect at all
I will give you an example to prevent my bad english cause misleading.
The following rewrite function makeAuthenticatedRequest() in callback deferred antipattern and promise fashion.
Callback:
Deferred antipattern:
function makeAuthenticatedRequest(){
// generate the appropriate auth Header;
var digestAuthHeader = generateDigestAuthHeader();
var promise = new Parse.promise();
Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
},
success: function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// create the final object to return in a promise
result = httpResponse.data;
console.log(result) // returns something not null!!!
// return a Promise that can be handled by any function
promise.resolve({'authData': authData, 'result': result});
},
error: function(error) {
console.error('Request failed with response code ' + error.status);
// it could be promise.resolve (success) or promise.reject (error)
promise.reject(error);
}
});
return promise;
}
Promise:
function makeAuthenticatedRequest(){
// generate the appropriate auth Header;
var digestAuthHeader = generateDigestAuthHeader();
return Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
}
}).then(function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// create the final object to return in a promise
result = httpResponse.data;
console.log(result) // returns something not null!!!
// return a Promise that can be handled by any function
return {'authData': authData, 'result': result};
}, function(error) {
console.error('Request failed with response code ' + error.status);
return Parse.Promise.error(error);
});
}

Categories

Resources