$q & promise service

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 whenFB.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 nexterror 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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值