I saw some examples of Facebook Login services that were using promises to access FB Graph API.
Example #1:
this.api = function(item) {
var deferred = $q.defer();
if (item) {
facebook.FB.api('/' + item, function (result) {
$rootScope.$apply(function () {
if (angular.isUndefined(result.error)) {
deferred.resolve(result);
} else {
deferred.reject(result.error);
}
});
});
}
return deferred.promise;
}
And services that used "$scope.$digest() // Manual scope evaluation" when got the response
Example #2:
angular.module('HomePageModule', []).factory('facebookConnect', function() {
return new function() {
this.askFacebookForAuthentication = function(fail, success) {
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
}
}
});
function ConnectCtrl(facebookConnect, $scope, $resource) {
$scope.user = {}
$scope.error = null;
$scope.registerWithFacebook = function() {
facebookConnect.askFacebookForAuthentication(
function(reason) { // fail
$scope.error = reason;
}, function(user) { // success
$scope.user = user
$scope.$digest() // Manual scope evaluation
});
}
}
JSFiddle
The questions are:
What is the difference in the examples above?
What are the reasons and cases to use $q service?
And how does it work?
This is not going to be a complete answer to your question, but hopefully this will help you and others when you try to read the documentation on the $q service. It took me a while to understand it.
Let's set aside AngularJS for a moment and just consider the Facebook API calls. Both the API calls use a callback mechanism to notify the caller when the response from Facebook is available:
facebook.FB.api('/' + item, function (result) {
if (result.error) {
// handle error
} else {
// handle success
}
});
// program continues while request is pending
...
This is a standard pattern for handling asynchronous operations in JavaScript and other languages.
One big problem with this pattern arises when you need to perform a sequence of asynchronous operations, where each successive operation depends on the result of the previous operation. That's what this code is doing:
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
First it tries to log in, and then only after verifying that the login was successful does it make the request to the Graph API.
Even in this case, which is only chaining together two operations, things start to get messy. The method askFacebookForAuthentication accepts a callback for failure and success, but what happens when FB.login succeeds but FB.api fails? This method always invokes the success callback regardless of the result of the FB.api method.
Now imagine that you're trying to code a robust sequence of three or more asynchronous operations, in a way that properly handles errors at each step and will be legible to anyone else or even to you after a few weeks. Possible, but it's very easy to just keep nesting those callbacks and lose track of errors along the way.
Now, let's set aside the Facebook API for a moment and just consider the Angular Promises API, as implemented by the $q service. The pattern implemented by this service is an attempt to turn asynchronous programming back into something resembling a linear series of simple statements, with the ability to 'throw' an error at any step of the way and handle it at the end, semantically similar to the familiar try/catch block.
Consider this contrived example. Say we have two functions, where the second function consumes the result of the first one:
var firstFn = function(param) {
// do something with param
return 'firstResult';
};
var secondFn = function(param) {
// do something with param
return 'secondResult';
};
secondFn(firstFn());
Now imagine that firstFn and secondFn both take a long time to complete, so we want to process this sequence asynchronously. First we create a new deferred object, which represents a chain of operations:
var deferred = $q.defer();
var promise = deferred.promise;
The promise property represents the eventual result of the chain. If you log a promise immediately after creating it, you'll see that it is just an empty object ({}). Nothing to see yet, move right along.
So far our promise only represents the starting point in the chain. Now let's add our two operations:
promise = promise.then(firstFn).then(secondFn);
The then method adds a step to the chain and then returns a new promise representing the eventual result of the extended chain. You can add as many steps as you like.
So far, we have set up our chain of functions, but nothing has actually happened. You get things started by calling deferred.resolve, specifying the initial value you want to pass to the first actual step in the chain:
deferred.resolve('initial value');
And then...still nothing happens. To ensure that model changes are properly observed, Angular doesn't actually call the first step in the chain until the next time $apply is called:
deferred.resolve('initial value');
$rootScope.$apply();
// or
$rootScope.$apply(function() {
deferred.resolve('initial value');
});
So what about error handling? So far we have only specified a success handler at each step in the chain. then also accepts an error handler as an optional second argument. Here's another, longer example of a promise chain, this time with error handling:
var firstFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'firstResult';
}
};
var secondFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'secondResult';
}
};
var thirdFn = function(param) {
// do something with param
return 'thirdResult';
};
var errorFn = function(message) {
// handle error
};
var deferred = $q.defer();
var promise = deferred.promise.then(firstFn).then(secondFn).then(thirdFn, errorFn);
As you can see in this example, each handler in the chain has the opportunity to divert traffic to the next error handler instead of the next success handler. In most cases you can have a single error handler at the end of the chain, but you can also have intermediate error handlers that attempt recovery.
To quickly return to your examples (and your questions), I'll just say that they represent two different ways to adapt Facebook's callback-oriented API to Angular's way of observing model changes. The first example wraps the API call in a promise, which can be added to a scope and is understood by Angular's templating system. The second takes the more brute-force approach of setting the callback result directly on the scope, and then calling $scope.$digest() to make Angular aware of the change from an external source.
The two examples are not directly comparable, because the first is missing the login step. However, it's generally desirable to encapsulate interactions with external APIs like this in separate services, and deliver the results to controllers as promises. That way you can keep your controllers separate from external concerns, and test them more easily with mock services.
I expected a complex answer that will cover both: why they are used in
general and how to use it in Angular
This is the plunk for angular promises MVP (minimum viable promise): http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview
Source:
(for those too lazy to click on the links)
index.html
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="myModule" ng-controller="HelloCtrl">
<h1>Messages</h1>
<ul>
<li ng-repeat="message in messages">{{ message }}</li>
</ul>
</body>
</html>
app.js
angular.module('myModule', [])
.factory('HelloWorld', function($q, $timeout) {
var getMessages = function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(['Hello', 'world']);
}, 2000);
return deferred.promise;
};
return {
getMessages: getMessages
};
})
.controller('HelloCtrl', function($scope, HelloWorld) {
$scope.messages = HelloWorld.getMessages();
});
(I know it doesn't solve your specific Facebook example but I find following snippets useful)
Via: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
Update 28th Feb 2014: As of 1.2.0, promises are no longer resolved by templates.
http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
(plunker example uses 1.1.5.)
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.
A promise provides an interface for interacting with it’s related deferred, and so, allows for interested parties to get access to the state and the result of the deferred operation.
When creating a deferred, it’s state is pending and it doesn’t have any result. When we resolve() or reject() the deferred, it changes it’s state to resolved or rejected. Still, we can get the associated promise immediately after creating a deferred and even assign interactions with it’s future result. Those interactions will occur only after the deferred rejected or resolved.
use promise within a controller and make sure the data is available or not
var app = angular.module("app",[]);
app.controller("test",function($scope,$q){
var deferred = $q.defer();
deferred.resolve("Hi");
deferred.promise.then(function(data){
console.log(data);
})
});
angular.bootstrap(document,["app"]);
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<div ng-controller="test">
</div>
</body>
</html>
Related
Forgive my ignorance, I'm new to Angular and JavaScript.
I've read that promises make it more elegant to call asynchronous services, but I've not been able to find any documentation on how to make my service asynchronous.
If you are simply wrapping a call to $http then there is no additional work needed on your part. All the $http calls return a promise, so you can just pass that along and your API will be async, and promise based.
function MyService($http){
this.$http = $http;
}
MyService.prototype = {
getContacts: function(){
//$http returns a promise already,
// so just pass it along
return $http.get('/api/contacts');
}
};
angular.service('myService', MyService);
//Later on in some controller
function MyController(myService){
var _this = this;
//getContacts() returns the promise
// from $http
myService.getContacts()
.success(function(contacts){
_this.contacts = contacts;
});
}
However...
If you want to create a normal API that executes code asynchronously, then you can wrap that up in a promise just like the example in the docs.
JavaScript is basically* single threaded, so that means that it uses an execution queue for asynchronous code. Execution will go from top to bottom, and whenever it stops, the queue will be emptied out in order.
When you call something like setTimeout or setInterval it is simply placing that code on the queue to be executed whenever the current thread gets around to it, but it isn't happening in the background.
You can test this for yourself, by opening up a browser console and typing this code into it:
setTimeout(function(){ console.log("I'm first!"); }, 0);
console.log("Nope, I am!");
Even though I have set the timeout to be 0 milliseconds, that doesn't mean it will execute immediately. It means it will be placed on the queue to be run immediately once all the other code finishes.
Finally
Try not to think of promises as strictly for managing asynchronous calls. They are a pattern for executing code once some precondition has been met. It just so happens that the most popular precondition is some asynchronous I/O via AJAX.
But the pattern is a great way of managing any operation that must wait for some number of preconditions.
Just to really drive this point home, check out this little snippet that uses a promise to determine when the user has clicked the button more than 5 times in a row.
(function() {
var app = angular.module('promise-example', []);
function PromiseBasedCounter($q, $timeout) {
this.$q = $q;
this.$timeout = $timeout;
this.counter = 0;
this.counterDef = $q.defer();
}
PromiseBasedCounter.$inject = ['$q', '$timeout'];
PromiseBasedCounter.prototype = {
increment: function() {
var _this = this;
//$timeout returns a promise, so we can
// just pass that along. Whatever is returned
// from the inner function will be the value of the
// resolved promise
return _this.$timeout(function() {
_this.counter += 1;
//Here we resolve the promise we created in the
// constructor only if the count is above 5
if (_this.counter > 5) {
_this.counterDef.resolve("Counter is above 5!");
}
return _this.counter;
});
},
countReached: function() {
//All defered objects have a 'promise' property
// that is an immutable version of the defered
// returning this means we can attach callbacks
// using the promise syntax to be executed (or not)
// at some point in the future.
return this.counterDef.promise;
}
};
app.service('promiseBasedCounter', PromiseBasedCounter);
function ClickCtrl(promiseBasedCounter) {
var _this = this;
_this.promiseBasedCounter = promiseBasedCounter;
_this.count = 0;
//Here we set up our callback. Notice this
// really has nothing to do with asyncronous I/O
// but we don't know when, or if, this code will be
// run in the future. However, it does provide an elegant
// way to handle some precondition without having to manually
// litter your code with the checks
_this.promiseBasedCounter.countReached()
.then(function(msg) {
_this.msg = msg;
});
}
ClickCtrl.$inject = ['promiseBasedCounter'];
ClickCtrl.prototype = {
incrementCounter: function() {
var _this = this;
//Whenever we increment, our service executes
// that code using $timeout and returns the promise.
// The promise is resolved with the current value of the counter.
_this.promiseBasedCounter.increment()
.then(function(count) {
_this.count = count;
});
}
};
app.controller('clickCtrl', ClickCtrl);
}());
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div ng-app="promise-example" ng-controller="clickCtrl as ctrl">
<div class="container">
<h1>The current count is: <small>{{ctrl.count}}</small></h1>
<button type="button" class="btn btn-lg btn-primary" ng-click="ctrl.incrementCounter()">Click Me!</button>
</div>
<div class="container">
<h1>{{ctrl.msg}}</h1>
</div>
</div>
It seems that factory methods execution priority is the highest, so that callbacks has no data to deal with. What is the best way to make this work?
I got this kind of factory
app.factory('jsonService', function($http) {
return {
getDistricts: function(callback) {
$http.get('data/districts.json').success(callback);
},
getLocations: function(path,callback) {
$http.get('data/'+path+'.json').success(callback);
}
};
});
And controller
var app = angular.module('sandbox', []);
app.controller('sandboxCtrl',function ($scope,jsonService) {
//This one works
$scope.init1= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
})
});
};
$scope.init1();
//This one does not
$scope.init2= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
})
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
});
};
$scope.init2();
});
Here is working plunker
Angular has an implementation of promises named $q (documentation) that you should read up upon.
There is a race condition due to the async nature of http calls. Please review the updated code linked to below that shows an example of your code running (successfully) using promises to handle your two calls in succession.
So upon success of your first call it will call your second service method all without using callbacks thanks to the power of promises.
jsonService.getDistricts()
.success(function(data) {
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict)
.success(function(locationData) {
$scope.locations = locationData;
})
});
updated PLKR
Promise clarification:
The raw implementation of basic promises uses then to handle responses and promises returned from $http add additional methods (success, error) that will unpack your data from the response object that you would need to handle if your just use then.
init1() is the correct way of doing this. init2() does work because jsonService.getLocations() is getting invoked before jsonService.getDistritcs() completes. The angular $http service is asynchronous. Since jsonService.getLocations() depends on data from jsonServicd.getDistricts() you must wait until .getDistricts() completes before calling .getLocations(). One way to do that is to call .getLocations() within the .getDitricts() callback, just as you did in init1().
I want to create an angular service that grabs data at "start-up" and builds up an array that will not change for the lifetime of the application. I don't want to return the promise of the http request. Caching the result would still incurs the cost of the 'someExpensiveFunctionCall'. What's the best way to handle this situation that can guarantee that the service will have the result populated and can be used in a controller and/or another service.
angular.module('app')
.service('MyService', function($http, defaults) {
var result = [];
$http.get('/some/url').then(function(data) {
if(angular.isDefined(data)) {
_.forEach(data, function(item) {
result.push(someExpensiveFunctionCall(item.value));
});
} else {
result = angular.copy(defaults.defaultResult);
}
});
return {
result: result
};
})
.controller(function(MyService) {
var someData = MyService.result;
})
.service(function(MyService) {
var someData = MyService.result;
});
I don't want to return the promise of the http request.
It seems like you want to do something that is asynchronous; synchronously. This can't work (reliably) without some sort of callback, promise, sync XHR, or other mechanism.
Knowing that we can evaluate some options. (in order of most practical)
Use your routers resolve property. This will complete some promise returning operation before instantiating your controller. It also (in ui-router and ngRoute) means the result of the promise will be injected into the controller under the specified key name.
Use the promise.
(aka #1 manually)
.factory('MyService',function($http,defaults){
var ret = {result: []};
ret.promise = $http.get('url').then(function(data){
if(angular.isDefined(data)){
_.forEach(data,function(item){
ret.result.push(someExpensiveFunctionCall(item.value));
});
}else{
_.forEach(angular.copy(defaults.defaultResult),function(val){
ret.result.push(val);
});
}
return ret;
});
return ret;
})
.controller('MyCtrl',function(MyService){
MyService.promise.then(myCtrl);
function myCtrl(){
var result = MyService.result
}
})
Lazy Evaluation. --> this is a little slow in angular due to dirty checking. But essentially means that you don't immediately manipulate/read result inside the body of a controller or service. But instead, use the view bindings, filters and interactions to call scope functions that act on the data. Since the resolution of a promise will invoke a digest cycle. Your view will render when the data arrives.
There was a bug in your code if the data happens to be undefined. You are de-referencing result by setting it to a copy of the defaults.
So all references to MyService.result will always be an empty array and never the default value.
In order to maintain the reference I'd loop over the defaults and add them to result with result.push.
Hope this helps!
I am using some data which is from a RESTful service in multiple pages.
So I am using angular factories for that. So, I required to get the data once from the server, and everytime I am getting the data with that defined service. Just like a global variables. Here is the sample:
var myApp = angular.module('myservices', []);
myApp.factory('myService', function($http) {
$http({method:"GET", url:"/my/url"}).success(function(result){
return result;
});
});
In my controller I am using this service as:
function myFunction($scope, myService) {
$scope.data = myService;
console.log("data.name"+$scope.data.name);
}
Its working fine for me as per my requirements.
But the problem here is, when I reloaded in my webpage the service will gets called again and requests for server. If in between some other function executes which is dependent on the "defined service", It's giving the error like "something" is undefined. So I want to wait in my script till the service is loaded. How can I do that? Is there anyway do that in angularjs?
You should use promises for async operations where you don't know when it will be completed. A promise "represents an operation that hasn't completed yet, but is expected in the future." (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)
An example implementation would be like:
myApp.factory('myService', function($http) {
var getData = function() {
// Angular $http() and then() both return promises themselves
return $http({method:"GET", url:"/my/url"}).then(function(result){
// What we return here is the data that will be accessible
// to us after the promise resolves
return result.data;
});
};
return { getData: getData };
});
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
// this is only run after getData() resolves
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
}
Edit: Regarding Sujoys comment that
What do I need to do so that myFuction() call won't return till .then() function finishes execution.
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
console.log("This will get printed before data.name inside then. And I don't want that.");
}
Well, let's suppose the call to getData() took 10 seconds to complete. If the function didn't return anything in that time, it would effectively become normal synchronous code and would hang the browser until it completed.
With the promise returning instantly though, the browser is free to continue on with other code in the meantime. Once the promise resolves/fails, the then() call is triggered. So it makes much more sense this way, even if it might make the flow of your code a bit more complex (complexity is a common problem of async/parallel programming in general after all!)
for people new to this you can also use a callback for example:
In your service:
.factory('DataHandler',function ($http){
var GetRandomArtists = function(data, callback){
$http.post(URL, data).success(function (response) {
callback(response);
});
}
})
In your controller:
DataHandler.GetRandomArtists(3, function(response){
$scope.data.random_artists = response;
});
I was having the same problem and none if these worked for me. Here is what did work though...
app.factory('myService', function($http) {
var data = function (value) {
return $http.get(value);
}
return { data: data }
});
and then the function that uses it is...
vm.search = function(value) {
var recieved_data = myService.data(value);
recieved_data.then(
function(fulfillment){
vm.tags = fulfillment.data;
}, function(){
console.log("Server did not send tag data.");
});
};
The service isn't that necessary but I think its a good practise for extensibility. Most of what you will need for one will for any other, especially when using APIs. Anyway I hope this was helpful.
FYI, this is using Angularfire so it may vary a bit for a different service or other use but should solve the same isse $http has. I had this same issue only solution that fit for me the best was to combine all services/factories into a single promise on the scope. On each route/view that needed these services/etc to be loaded I put any functions that require loaded data inside the controller function i.e. myfunct() and the main app.js on run after auth i put
myservice.$loaded().then(function() {$rootScope.myservice = myservice;});
and in the view I just did
ng-if="myservice" ng-init="somevar=myfunct()"
in the first/parent view element/wrapper so the controller can run everything inside
myfunct()
without worrying about async promises/order/queue issues. I hope that helps someone with the same issues I had.
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.