deferred - AngularJS : Where to use promises? -
i saw examples of facebook login services using promises 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 used "$scope.$digest() // manual scope evaluation"
when got 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 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 }); } }
the questions are:
- what difference in examples above?
- what reasons , cases use $q service?
- and how work?
this not going complete answer question, , others when try read documentation on $q
service. took me while understand it.
let's set aside angularjs moment , consider facebook api calls. both api calls use callback mechanism notify caller when response facebook available:
facebook.fb.api('/' + item, function (result) { if (result.error) { // handle error } else { // handle success } }); // program continues while request pending ...
this standard pattern handling asynchronous operations in javascript , other languages.
one big problem pattern arises when need perform sequence of asynchronous operations, each successive operation depends on result of previous operation. that's code doing:
fb.login(function(response) { if (response.authresponse) { fb.api('/me', success); } else { fail('user cancelled login or did not authorize.'); } });
first tries log in, , after verifying login successful make request graph api.
even in case, chaining 2 operations, things start messy. method askfacebookforauthentication
accepts callback failure , success, happens when fb.login
succeeds fb.api
fails? method invokes success
callback regardless of result of fb.api
method.
now imagine you're trying code robust sequence of 3 or more asynchronous operations, in way handles errors @ each step , legible else or after few weeks. possible, it's easy keep nesting callbacks , lose track of errors along way.
now, let's set aside facebook api moment , consider angular promises api, implemented $q
service. pattern implemented service attempt turn asynchronous programming resembling linear series of simple statements, ability 'throw' error @ step of way , handle @ end, semantically similar familiar try/catch
block.
consider contrived example. have 2 functions, second function consumes result of first one:
var firstfn = function(param) { // param return 'firstresult'; }; var secondfn = function(param) { // param return 'secondresult'; }; secondfn(firstfn());
now imagine firstfn , secondfn both take long time complete, want process sequence asynchronously. first create new deferred
object, represents chain of operations:
var deferred = $q.defer(); var promise = deferred.promise;
the promise
property represents eventual result of chain. if log promise after creating it, you'll see empty object ({}
). nothing see yet, move right along.
so far our promise represents starting point in chain. let's add our 2 operations:
promise = promise.then(firstfn).then(secondfn);
the then
method adds step chain , returns new promise representing eventual result of extended chain. can add many steps like.
so far, have set our chain of functions, nothing has happened. things started calling deferred.resolve
, specifying initial value want pass first actual step in chain:
deferred.resolve('initial value');
and then...still nothing happens. ensure model changes observed, angular doesn't call first step in chain until next time $apply
called:
deferred.resolve('initial value'); $rootscope.$apply(); // or $rootscope.$apply(function() { deferred.resolve('initial value'); });
so error handling? far have specified success handler @ each step in chain. then
accepts error handler optional second argument. here's another, longer example of promise chain, time error handling:
var firstfn = function(param) { // param if (param == 'bad value') { return $q.reject('invalid value'); } else { return 'firstresult'; } }; var secondfn = function(param) { // param if (param == 'bad value') { return $q.reject('invalid value'); } else { return 'secondresult'; } }; var thirdfn = function(param) { // 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 can see in example, each handler in chain has opportunity divert traffic next error handler instead of next success handler. in cases can have single error handler @ end of chain, can have intermediate error handlers attempt recovery.
to return examples (and questions), i'll represent 2 different ways adapt facebook's callback-oriented api angular's way of observing model changes. first example wraps api call in promise, can added scope , understood angular's templating system. second takes more brute-force approach of setting callback result directly on scope, , calling $scope.$digest()
make angular aware of change external source.
the 2 examples not directly comparable, because first missing login step. however, it's desirable encapsulate interactions external apis in separate services, , deliver results controllers promises. way can keep controllers separate external concerns, , test them more mock services.
Comments
Post a Comment