I still can't understand the role of using the $q service, (what exactly will it add) if you want to create a service that need to call only one API via http , in this situation I don't know why shouldn't I just do the following (without using $q) :
this.getMovie = function(movie) {
return $http.get('/api/v1/movies/' + movie)
.then(
function(response) {
return {
title: response.data.title,
cost: response.data.price
});
},
function(httpError) {
// translate the error
throw httpError.status + " : " +
httpError.data;
});
};
Very good question and one very few people appreciate the answer to.
Consider this:
this.getMovie = function(movie) {
return $http.get('/api/v1/movies/' + movie);
};
Great code but these rules apply:
$http will resolve for 2xx responses and will otherwise reject. sometimes we don't want this. we want to reject on resolution and resolve on rejection. This makes sense when you consider a HEAD request to check the existence of something.
A HEAD request to /book/fred returning 200 shows book fred exists. But if my function is testing whether book fred is unique, it is not and so we want to reject on a 200. this is where $q comes in. Now I can do this:
var defer = $q.defer();
$http.head('/book/fred').then(function(response) {
// 2xx response so reject because its not unique
defer.reject(response);
}).catch(function(failResponse) {
defer.resolve(failResponse);
});
return defer.promise;
$q gives me total control of when I reject AND when I resolve.
Also, $q allows me to reject or resolve with any value. So if I am only interested in some of the response I can resolve with just the bit I am interested in.
Finally, I can use $q to turn non-promise based code into a promise.
var a = 5;
var b = 10;
var defer = $q.defer();
var resolve(a+b);
return defer.promise;
Bosh, if I need a promise as my return value then I've got one.
This is also great when mocking for unit tests.
AngularJS services such as $http, $timeout, $resource, etc. use the $q service internally to generate their promises. With those services there generally is no need to inject the $q service. In fact if you see $q.defer being used with those services, you should be asking Is this a “Deferred Antipattern”?.
There are some methods of the $q service that are useful in certain circumstances.
The $q.all method is used to wait on several promises.
var promise1 = $http.get(url1);
var promise2 = $http.get(url2);
$q.all([promise1, promise2]).then( responseArray ) {
$scope.data1 = responseArray[0].data;
$scope.data2 = responseArray[1].data;
}).catch( function ( firstError ) {
console.log(firstError.status)
});
The .catch method can be used to convert a rejected promise to a fulfilled promise. And vice versa with the .then method. No need to use $q.defer for that. For more information, see Angular execution order with $q.
The $q.when method is useful for generating a promise from unknown sources.
var promise = $q.when(ambiguousAPI(arg1));
The $q.when method creates a $q promise in all cases whether ambiguousAPI returns a value, a $q promise, or a promise from another library.
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
To summarize: The $q service is used to create a promise, so when using service (like $http,$timeout,$resource,etc.) that already return promises you generally don't need to use the $q service.
In this case you certainly don't need it because $http.get itself returns a promise. But for example if you perform async call only on some condition it is useful
function acyncService () {
if (dataLoaded) return $q.resolve(data);
return $http.get('path/to/load/data');
}
In this case even if you do not perform async call, you still can use
acyncService().then(function(data){
console.log(data);
});
This is only one of many examples. It is also useful to use $q promises when you do async requests with other libs like AWS SDK for instance.
$http does an asynchronous call. So, ideally if you want to get the value of the response in the url, you should use a '$scope' variable to store it.
$http("your url").then(function(response){
//This is the success callback
$scope.movies = response.data;
});
Related
I am using angularJS and trying to creating amazon VPC through aws.sdk.js
Now when I create VPC I have to done various configuration and call below methods in chain
create vpc () => vpc_
create internet gateway () => igw_
attach internet gateway (vpc_, igw_) =>
create subnet (vpc_) => subnet_
create route table (vpc_) => rtb_
associate route table (rtb_, subnet_) =>
create security group (vpc_) => sg_
add inbound rule (sg_) =>
additionally create tags for each generated resources .
as you can see some function parameter depends on previous function and so on. I am using $q services on each AWS method but this is became so large callback hell.
I also capture notifications of each function in back so my input function became like this
functionOne().then(function(res1) {
console.info(res1);
functionTwo(res1.id).then(res2) {
console.info(res2);
......... series of functions within function....
----------------
----------------
.then(res7) { functionEight(res7.id) {
}, function (err) {}, function (notify) {})
.finally(function(){ console.log('Finally done everything'); });
---------------------
---------------------
}, function (err2) {
console.error(err2);
}, function (notify2) {
console.log(nofify2);
});
}, function (err1) {
console.error(err1);
}, function (notify1) {
console.log(nofify1);
});
my function signature is as below
functionX: function() {
var d = $q.defer();
var params = {
//...
};
ec2.anyMethodName(params, function(err, data) {
if (err) {
d.reject(err);
} else {
d.notify('Notifications methods');
d.resolve(data);
}
});
return d.promise;
}
You can see the chaining of methods in my controller here
So my question is
Does Amazon native thenable promise property can be used in angular? if yes then how do we implement notifications?
Is there any sorter way to do that? like error will be captures in last? using $.all() but do not not how?
If i use .finally() on functionOne than it throw error in console
.finally is not a function
Why don't you
functionOne()
.catch(errorHandlerOne)
.then(functionTwo) // calls functionTwo(responseOne)
.catch(errorHandlerTwo)
.then(functionThree) // calls functionThree(responseTwo)
.catch(errorHandlerThree)
.finally(finalHandler)
Edit: If you want to access the result of functionOne in functionThree, you can do the following:
functionTwo(resultOne) {
var promise = d.promise;
promise.then(function(resultTwo) {
return [resultOne, resultTwo];
});
return d.promise
}
Refer to this answer for more details.
You don't need to necessarily have to write anonymous callbacks as promise resolve and reject handlers
Your callbacks will be called with the resolved/rejected promise from the previous function automatically.
If you want your entire chain to fail if any one promise fails, you call always use .all(), so that's something you should pay attention to if you want to use .all().
To chain promises, return values to the success handlers; throw values to the rejection handlers:
functionOne().then(function onSuccess(res1) {
console.info(res1);
//return promise to chain
return functionTwo(res1.id);
}).catch(function onReject(error1) {
console.log(error1)
//throw to chain rejection
throw error1;
}).then(function onSuccess2(res2) {
console.info(res2);
//return promise to chain
return functionThree(res2.id);
}).then(function onSuccess3(res3) {
console.info(res3);
//return promise to chain
return functionFour(res3.id);
}).then(function onSuccess4(res4) {
console.info(res4);
//return to chain
return res4.id;
});
It is very important to throw to rejection handlers. If a rejection handler simply does a console log and omits the throw statement, the function returns undefined and the rejection gets converted. The next success handled will be invoked with undefined as its argument.
From the Docs:
Chaining promises
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs
--AngularJS $q Service API Reference -- Chaining Promises
For more information, see SO: Angular execution order with $q -- Chaining Promises
Does Amazon native thenable promise property can be used in angular?
Promises from an external source such as the AWS API can be converted to a $q service with $q.when.
var angularPromise = $q.when(AWS.request.promise());
From the DOCS:
when
Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.
--AngularJS $q Service API Reference -- $q.when
I'm calling the following service:
Get accounts details
First piece of code in my controller:
//resp is coming from an $http sync request, above the below request.
for (var i = 0; i < resp.rows; ++i) {
userService.getAccountsBalance($rootScope.userid, resp[i]['Acct_Number']).then(function(data){
console.log(data.bal) // shows two duplicate balances
});
}
In my service:
app.service('userService', function($rootScope, $q){
var deferred = $q.defer();
this.getAccountsBalance = function(userid, accountNum){
console.log(userid + " " + accountNum)
var req = <my $http request>
req.send().then(function(resp){
deferred.resolve(resp.responseJSON);
});
console.log(deferred.promise) //// prints two balances JSON objects with no duplicate
return deferred.promise;
}
});
My question is, I can see that two requests(with different parameter) has been executed in my service, and returning two different balances for two accounts. However, in my controller I get two duplicate results. I only get the last response twice.
I'm pretty sure it has something to do with promises, and I'm still new.
The problem seems to be you are returning the same promise for both requests,
app.service('userService', function($rootScope, $q){
this.getAccountsBalance = function(userid, accountNum){
var deferred = $q.defer();
Change it like above and it should work.
In order to loop through promises, you should use the $q service which provide the $.all method that allows you to resolve only when all promises has been resolved, and then get the data for each promise
When there are multiple promises are involved,if anyone promise get resolved all promise will get resolved with same value.
So you should use $q.all() to get result of all promises.
You can do in this way.
Inject $q Service.
var arrAllPromise = [];
for (var i = 0; i < resp.rows; ++i) {
arrAllPromise[i] = userService.getAccountsBalance($rootScope.userid, resp[i]['Acct_Number'])
}
$q.all(arrAllPromise).then(function(data) {
console.log(data) // shows two duplicate balances
});
It will give all promise data with which it is resolved.
Basically problem was you were having common $q object for promise.When your 1st promise gets resolved, the outer $q promiseis resolved by some response.
Then you had 2nd call for the same function, it would make that $http call but return old resolved promise. Because old $q object has fulfilled its promise already.
For solving the problem, you should have separate $q.defer() inside method. So that each time proper data will return by service method
You are creating a custom promise using $q,which is considered as bad pattern. As you have $http.get method is there in place, you can utilize their promise(each $http method promise, and we can chain them using .then)
Service
app.service('userService', function($rootScope, $q){
this.getAccountsBalance = function(userid, accountNum){
var req = <my $http request>
//utilize promise object returned by $http
req.send().then(function(resp){
//return data that sent to promise chain function
return resp.data;
});
}
});
I have read every article on $q out there but somehow I am unable to grasp it. For example take the best article I found. I get as far as:
var myFirstDeferred = $q.defer(); //Actually I don't even get this far
A deferred represents the result of an asynchronic operation. It exposes an interface that can be used for signaling the state and the result of the operation it represents. It also provides a way to get the associated promise instance.
What's a 'deferred'? How is it different from a promise? Then we get to this:
async(function(value) {
myFirstDeferred.resolve(value);
}, function(errorReason) {
myFirstDeferred.reject(errorReason);
});
I have NO idea what this is doing. I want to stress that I understand async. I understand the promise structure. For example I know exactly what the code below is doing:
$http.get(url)
.then(function(result){returnresult}
,function(error){return error})
But what are we doing with the deferred in the object above? Why even have the deferred at all? Why not just the then block?
Edit: I want to stress that I went through a ton of replies here, as well as the articles. I thought the point of $q was the force the execution of an async call (kind of like "await" in c#) and then perform some code after. I really don't understand how it does that. I do get how the .all command works when waiting for multiple async operations in this example, but not with one.
Edit: This edit is in response to the duplicate suggestion - I disagree with the notion. For one, this question is more focused and limited in scope. In addition, the answer accepted here clarifies far better (imo) than the wide-net answer in the other q.
Typically you don't have to use a deferred explicitly. If you find yourself using $q.defer you should question why you are not using the promise interface directly. See the bluebird documentation for a description of why. $http and many other libraries use deferreds internally and return promises from the methods they expose so using deferreds on top of this is unnecessary.
That being said, deferreds can be useful and understanding them is important. Typically, deferreds have two methods: .reject and .resolve. Honestly there could be one method that you could use to mark the result of an asynchronous operation:
.reject -> the asynchronous operation failed or could not complete
.resolve -> the asynchronous operation completed successfully
When a deferred is completed, it triggers the promise callbacks.
You need to use $q.defer or deferreds when dealing with operations that are asynchronous but do not have a built in promise interface such as timers:
var dfd = $q.defer();
$timeout(function () {
// this is asynchronous
// it completed successfully
dfd.resolve("some value");
}, 500);
dfd.promise.then(function (value) {
assert.equal(value, "some value");
});
Rather than using deferreds as an object interface, Angular allows you to use these destructured as function arguments to $q
Rewriting the above:
var promise = $q(function (resolve) {
$timeout(function () {
resolve("some value");
}, 500);
});
promise.then(function (value) {
assert.equal(value, "some value");
});
A deferred object is just a subset of a promise that only gives you .reject and .resolve, which prevents the outside world from doing anyting else with it. If you can't return a promise (like when using a library built on callbacks, not promises), make a deferred and manually reject or resolve it.
deferred.reject and deferred.resolve only matter when you return the deferred. Basically:
somePromise().then( function() {
var deferred = q.defered();
someFunctionThatHasCallback( function() {
deferred.resolve();
});
return defererd;
}).then( function() {
console.log( 'I wait for deferred.resolve' );
});
If your functions return promises, you don't need deferred, because promises are chainable by returning inside .then
somePromise().then( function() {
return someFunctionThatReturnsPromise();
}).then( function() {
console.log( 'I wait for promise to resolve' );
});
Watching a lot of Egghead.io videos, I noticed that a common pattern is to return a custom promise and resolve it in the callbacks.
.factory('myFact', function($q, $http) {
return {
getData: function() {
var deferred = $q.defer();
$http.get('/path/to/api')
.success(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
};
});
I would normally write this as:
.factory('myFact', function($http) {
return {
getData: function() {
return $http.get('/path/to/api')
.then(function(res) {
return res.data;
});
}
};
});
Is there any advantage to returning a $q.defer() promise rather than an $http promise? The approaches look identical to me.
No, no advantages, it's the same, In your first code snipped you created a $q.defer() instance then you invoked its resolve() method to create a resolved promise.
That is the process you will need to know and pass throw in angularJs when working with asynchronously functions and future objects that will have different values or new data at some future moment which you will need to know when it happens because interested parties in your app may need to get access to the result of the deferred task when it completes.
Now when working with $http you don't have to do any of that because it will already return a resolved promise that you can directly invoke it's then() method unless you have a different way to do things and you need to implement a different approach.
But not all angularJs services are going to do the work for you, get a look to $resource for example, which wraps $http for use in RESTful web API scenarios. $resource will not return a resolved promise, a promise yes, you are getting one but you'll need to do the last step of resolving it (check this stack question or this and maybe this article about Amber Kaplan's own experience working with Rest).
So the way how you are doing it is good, that is how I'm doing it too when working with $http but the first snippet code is the one that we will be all searching for when we will need to do things differently with $http or forcing other services to 'work with' or 'work like' AJAX.
I'm writing a service that will retrieve data asynchronously ($http or $resource). I can hide the fact that it is asynchronous by returning an array that will initially be empty, but that will eventually get populated:
.factory('NewsfeedService1', ['$http', function($http) {
var posts = [];
var server_queried = false;
return {
posts: function() {
if(!server_queried) {
$http.get('json1.txt').success(
function(data) {
server_queried = true;
angular.copy(data, posts);
});
}
return posts;
}
};
}])
.controller('Ctrl1', ['$scope','NewsfeedService1',
function($scope, NewsfeedService1) {
$scope.posts = NewsfeedService1.posts();
}])
Or I can expose the asynchronicity by returning a promise:
.factory('NewsfeedService2', ['$http', function($http) {
var posts = [];
var server_queried = false;
var promise;
return {
posts_async: function() {
if(!promise || !server_queried) {
promise = $http.get('json2.txt').then(
function(response) {
server_queried = true;
posts = response.data;
return posts;
});
}
return promise;
}
};
}])
.controller('Ctrl2', ['$scope','NewsfeedService2',
function($scope, NewsfeedService2) {
NewsfeedService2.posts_async().then(
function(posts) {
$scope.posts = posts;
});
// or take advantage of the fact that $q promises are
// recognized by Angular's templating engine:
// (note that Peter and Pawel's AngularJS book recommends against this, p. 100)
$scope.posts2 = NewsfeedService2.posts_async();
}]);
(Plunker - if someone wants to play around with the above two implementations.)
One potential advantage of exposing the asychronicity would be that I can deal with errors in the controller by adding an error handler to the then() method. However, I'll likely be catching and dealing with $http errors in an application-wide interceptor.
So, when should a service's asynchronicity be exposed?
My guess is that you'll find people on both sides of this fence. Personally, I feel that you should always expose the asynchronicity of a library or function (or more correctly: I feel that you should never hide the asynchronicity of a library or function). The main reason is transparency; for example, will this work?
app.controller('MyController', function(NewsfeedService) {
$scope.posts = NewsfeedService.posts();
doSomethingWithPosts($scope.posts); // <-- will this work?
});
If you're using the first method (e.g. $resource), it won't, even though $scope.posts is technically an array. If doSomethingWithPosts has its own asynchronous operations, you could end up with a race condition. Instead, you have to use asynchronous code anyway:
app.controller('MyController', function(NewsfeedService) {
$scope.posts = NewsfeedService.posts(function() {
doSomethingWithPosts($scope.posts);
});
});
(Of course, you can make the callback accept the posts as an argument, but I still think it's confusing and non-standard.)
Luckily, we have promises, and the very purpose of a promise is to represent the future value of an operation. Furthermore, since promises created with Angular's $q libraries can be bound to views, there's nothing wrong with this:
app.controller('MyController', function(NewsfeedService) {
$scope.posts = NewsfeedService.posts();
// $scope.posts is a promise, but when it resolves
// the AngularJS view will work as intended.
});
[Update: you can no longer bind promises directly to the view; you must wait for the promise to be resolved and assign a scope property manually.]
As an aside, Restangular, a popular alternative to $resource, uses promises, and AngularJS' own $resource will be supporting them in 1.2 (they may already support them in the latest 1.1.x's).
I would always go with async option since i don't like hiding the async nature of the underlying framework.
The sync version may look more clean while consuming it, but it inadvertently leads to bug where the developer does not realize that the call is async in nature and tries to access data after making a call.
SO is filled with questions where people make this mistake with $resource considering it sync in nature, and expecting a response. $resource also takes similar approach to option 1, where results are filled after the call is complete, but still $resource exposes a success and failure function.
AngularJS tries to hide the complexities of async calls if promises are returned, so binding directly to a promise feels like one is doing a sync call.
I say no, because it makes it harder to work with multiple services built this way. With promises, you can use $q.all() to make multiple request and respond when all of them complete, or you can chain operations together by passing the promise around.
There would be no intuitive way to do this for the synchronous style service.