I am trying to reject a deferred object within a $http.get call but it is not being properly rejected. The errorcallback is not being called, and I just can't figure out why.
Here is basically what I have:
var promise = this.SomeAsyncCall(this.$.SomeID)
.then(
function ()
{
service.SendAsyncDownloadReqeuest(someOtherID);
},
this.ErrorHandler.HandleError)
.then(this._DownloadInitiated, this.ErrorHandler.HandleError);
promise["finally"](this._DownloadRequestFinished);
And here is the service.SendAsyncDownloadRequest:
var SendAsyncDownloadReqeuest = function (ID)
{
var url = "someUrl?ID=" + customerID;
var navigator = self.$window.navigator;
var window = self.$window;
var deferred = self.$q.defer();
self.$http.get(url, { responseType: 'arraybuffer' })
.success( function(data, status, headers) {
var success = false;
//Initiate download via blob. Set success
success ? deferred.resolve() : deferred.reject();
})
.error(function (data, status)
{
var error =
{
//Some error properties
}
deferred.reject(error);
});
return deferred.promise;
};
When I test this by returning a 500 status code from the server, it reaches the .error block of the http get call and completes the reject line, but the ErrorHandler's HandleError method is not reached. The HandleError method is correct since it works with errorcallbacks for promises rejected in anything that's not $http.get.
You're never passing the promise from service.SendAsyncDownloadReqeuest(someOtherID); back to your HandleError function. You need to change your code to this:
var promise = this.SomeAsyncCall(this.$.SomeID)
.then(
function ()
{
return service.SendAsyncDownloadReqeuest(someOtherID);
},
this.ErrorHandler.HandleError)
.then(this._DownloadInitiated, this.ErrorHandler.HandleError);
promise["finally"](this._DownloadRequestFinished);
If you want to be a little clearer you could change it to this:
var promise = this.SomeAsyncCall(this.$.SomeID)
.then(function () {
service.SendAsyncDownloadReqeuest(someOtherID).then(
this._DownloadInitiated,
this.ErrorHandler.HandleError);
},
this.ErrorHandler.HandleError);
promise["finally"](this._DownloadRequestFinished);
Don't use the success method either way.Both methods have been deprecated.
The $http legacy promise methods success and error have been
deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
Here is the shortcut method
$http.post('/someUrl', data, config).then(successCallback, errorCallback);
Here is a longer GET method sample
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Official Documentation
Related
In my Controller:
function login(credentials) {
AuthService
.login(credentials)
.then(successCallback, errorCallback);
//same issue with .then(successCallback).catch(errorCallback);
}
function successCallback() {
// do something after success
}
function errorCallback(data) {
// do something after error
}
and in my AuthService:
authService.login = function (credentials) {
return $http
.post(ENV.apiEndpoint + 'api/v1/login_check', credentials)
.then(
function (result) {
Session.create(result.data.token, result.data.data);
},
function (data) {
Messages.create('Login failed: ' + data.statusText);
}
);
}
When my POST delivers a 200 response code, everything works as expected do something after success is executed.
But when my POST results e.g. in a 401 I can see that Messages.create is called (so in this case it enters the error path), but unfortunately my Controller calls the successCallback and not the errorCallback.
I had to migrate this because I was using the deprecated and since Angular 1.6 removed .success and .error promise attributes. It was working back then, but after migration this doesn't work anymore.
What am I doing wrong here?
You may reject the promise in your error callback.
authService.login = function (credentials) {
return $http
.post(ENV.apiEndpoint + 'api/v1/login_check', credentials)
.then(
function (result) {
Session.create(result.data.token, result.data.data);
},
function (data) {
Messages.create('Login failed: ' + data.statusText);
return $q.reject(data);
}
);
}
From Angular $q doc:
reject(reason);
Creates a promise that is resolved as rejected with the specified
reason. This api should be used to forward rejection in a chain of
promises. If you are dealing with the last promise in a promise chain,
you don't need to worry about it.
When comparing deferreds/promises to the familiar behavior of
try/catch/throw, think of reject as the throw keyword in JavaScript.
This also means that if you "catch" an error via a promise error
callback and you want to forward the error to the promise derived from
the current promise, you have to "rethrow" the error by returning a
rejection constructed via reject.
I am pretty new to AngularJS and Javascript. I am creating a app in which I am planning to use one function to do all ajax related operation(factory in angularjs).
This function will be only one gateway for all ajax operations. When I return promise as a success it works properly. but returning error promise do not work.
Here is a sample code. I am expecting return of promise in error function if promise fails but it goes to success
var myApp = angular.module('myApp', []);
myApp.controller('FirstController, function($scope, util){
util.doAjax().then(function(response){
// This function is called in both success and error
}, function(err){
// This is never called why ?
});
});
myApp.factory('util, function($http){
return $http({
method: 'GET',
url: 'http://www.example.com'
}).then(function(response){
// This will return success promise
return response;
}, function(err){
// This should return error promise
return err;
});
});
Currently you are directly returning a data from error function, which is chaining the promise and calling it underlying .then method.
While returning an error you have to reject a promise by creating a new custom promise using $q
return $q.reject(err)
Other important thing is, you should create method in a service with name
myApp.factory('util', function($http, $q){
//Exposed method
return {
doAjax : function() {
return $http({
method: 'GET',
url: 'http://www.example.com'
}).then(function(response){
// This will return success promise
return response.data; // returned data
}, function(err){
// return error promise with err object
return $q.reject(err);
});
}
}
});
This way you are not returning a promise.
You must return the $http promise instead like so:
myApp.factory('util', function($http){
return {
ajax: function () {
var httpPromise = $http({
method: 'GET',
url: 'http://www.example.com'
});
httpPromise.then(function(response){
// This will return success promise
// return response;
}, function(err){
// This should return error promise
// return err;
});
return httpPromise;
};
});
And also, return inside promise resolutions are not needed, you can use it for loggin and stuff but it doesn't require a return value because it is the resolution it self.
i've a function that return a boolean according to http.get call.
function checkValue (value) {
var defer = $q.defer();
var request = $http({
method: "get",
url: "someurl"
});
request.success(function (data) {
defer.resolve(data.response);
});
return defer.promise;
}
The problem is that return value is an object like this:
d {$$state: Object}
$$state: Object
status: 1
value: true
__proto__: Object
__proto__: d
How could i resolve it?
Yes, your function is returning a Promise object. The $$state property belongs to the promise object and is used by Angular internally (as suggested by $$ prefix) and therefore not to be used by developers using Angular. More interesting is that promise object has a "then" method which can be used to attach handlers that are called when the promise gets resolved. Promise gets resolved when the defer object related to the promise is resolved.
So, you could use your code as
checkValue.then(function (data) {
// This is called when the XHR response comes in and
// defer.resolve() is called in the checkValue function.
});
But, there is a better approach to this. Using promise chaining.
Which essentially removes the need to create a new defer object.
function checkValue (value) {
return $http({
method: "get",
url: "someurl"
}).then(function (data) {
return data.response;
});
}
Explaining everything about how Promises work is may be too much for this post. There are already awesome works done on that.
But, basically, $http already returns a promise. So you can just use this promise instead of creating a new one. The end usage of the function remains exactly the same as the snippet above.
BTW, .success() handlers are deprecated. So better start using .then() on your apps already.
Since you are using Promise, you need to use .then callback in order to get response or error:
function checkValue (value) {
var defer = $q.defer();
var request = $http({
method: "get",
url: "someurl"
});
request.success(function (data) {
defer.resolve(data.response);
});
return defer.promise;
}
var promise = checkValue('Hello World');
promise.then(function(response) {
//success
console.log(response);
}, function(reason) {
//failed
console.log(reason);
});
then(successCallback, errorCallback, notifyCallback) – regardless of
when the promise was or will be resolved or rejected, then calls one
of the success or error callbacks asynchronously as soon as the result
is available. The callbacks are called with a single argument: the
result or rejection reason. Additionally, the notify callback may be
called zero or more times to provide a progress indication, before the
promise is resolved or rejected.
But the optimized approach would be using $http's inbuilt promise instead of creating a new one.
var request = $http({
method: 'GET',
url: '/someUrl'
});
request.then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
You should try this,
function checkValue (value) {
return $q(resolve, reject) {
$http({
method: "get",
url: "someurl"
}).success(function (data) {
resolve(data.response);
}).then (function (error) {
reject(error);
});
}
}
This will resolve your data
I am trying to work through JS Promises in node.js and don't get the solution for passing promises between different function.
The task
For a main logic, I need to get a json object of items from a REST API. The API handling itself is located in a api.js file.
The request to the API inthere is made through the request-promise module. I have a private makeRequest function and public helper functions, like API.getItems().
The main logic in index.js needs to wait for the API function until it can be executed.
Questions
The promise passing kind of works, but I am not sure if this is more than a coincidence. Is it correct to return a Promise which returns the responses in makeRequest?
Do I really need all the promises to make the main logic work only after waiting for the items to be setup? Is there a simpler way?
I still need to figure out, how to best handle errors from a) the makeRequest and b) the getItems functions. What's the best practice with Promises therefor? Passing Error objects?
Here is the Code that I came up with right now:
// index.js
var API = require('./lib/api');
var items;
function mainLogic() {
if (items instanceof Error) {
console.log("No items present. Stopping main logic.");
return;
}
// ... do something with items
}
API.getItems().then(function (response) {
if (response) {
console.log(response);
items = response;
mainLogic();
}
}, function (err) {
console.log(err);
});
api.js
// ./lib/api.js
var request = require('request-promise');
// constructor
var API = function () {
var api = this;
api.endpoint = "https://api.example.com/v1";
//...
};
API.prototype.getItems = function () {
var api = this;
var endpoint = '/items';
return new Promise(function (resolve, reject) {
var request = makeRequest(api, endpoint).then(function (response) {
if (200 === response.statusCode) {
resolve(response.body.items);
}
}, function (err) {
reject(false);
});
});
};
function makeRequest(api, endpoint) {
var url = api.endpoint + endpoint;
var options = {
method: 'GET',
uri: url,
body: {},
headers: {},
simple: false,
resolveWithFullResponse: true,
json: true
};
return request(options)
.then(function (response) {
console.log(response.body);
return response;
})
.catch(function (err) {
return Error(err);
});
}
module.exports = new API();
Some more background:
At first I started to make API request with the request module, that works with callbacks. Since these were called async, the items never made it to the main logic and I used to handle it with Promises.
You are missing two things here:
That you can chain promises directly and
the way promise error handling works.
You can change the return statement in makeRequest() to:
return request(options);
Since makeRequest() returns a promise, you can reuse it in getItems() and you don't have to create a new promise explicitly. The .then() function already does this for you:
return makeRequest(api, endpoint)
.then(function (response) {
if (200 === response.statusCode) {
return response.body.items;
}
else {
// throw an exception or call Promise.reject() with a proper error
}
});
If the promise returned by makeRequest() was rejected and you don't handle rejection -- like in the above code --, the promise returned by .then() will also be rejected. You can compare the behaviour to exceptions. If you don't catch one, it bubbles up the callstack.
Finally, in index.js you should use getItems() like this:
API.getItems().then(function (response) {
// Here you are sure that everything worked. No additional checks required.
// Whatever you want to do with the response, do it here.
// Don't assign response to another variable outside of this scope.
// If processing the response is complex, rather pass it to another
// function directly.
}, function (err) {
// handle the error
});
I recommend this blog post to better understand the concept of promises:
https://blog.domenic.me/youre-missing-the-point-of-promises/
When using JQuery.Deferred is it OK to invoke reject() directly? Without having invoked a async function?
Perhaps I want some kind of test in the beginning of my async function. If the test fails I want to reject immediately. See the first if block below.
function doSomethingAsync() {
//Test if the ajax call should be invoked
var testFailed = true;
var dfd = $.Deferred();
//Check if test failed
if (testFailed) {
var asyncResult = {
success: false,
data: 'test failed'
};
//Is this OK usage of reject on the same thread?
dfd.reject(asyncResult);
return dfd.promise();
}
$.get('/api/testapi/get').done(function (data) {
var asyncResult = {
success: true,
data: data
};
dfd.resolve(asyncResult);
}).fail(function (err) {
var asyncResult = {
success: false,
data: err
};
dfd.reject(asyncResult);
});
return dfd.promise();
}
When using JQuery.Deferred is it OK to invoke reject() directly? Without having invoked a async function?
Yes, it's totally OK to return an already rejected promise, and to reject deferreds immediately. You only might need to verify that your callbacks don't rely on asynchronous resolution, which jQuery does not guarantee (in contrast to A+ implementations).
Notice that in your code you should use then instead of manually resolving the deferred:
function doSomethingAsync() {
var testFailed = /* Test if the ajax call should be invoked */;
var dfd = testFailed
? $.Deferred().reject('test failed')
: $.get('/api/testapi/get');
return dfd.then(function (data) {
return {
success: true,
data: data
};
}, function (err) {
return {
success: false,
data: err
};
});
}
You can do it quickly, as your function return a Promise object:
return Promise.reject('test failed');