AngularJS 用promises和$q处理异步调用

AngluarJS $q 是受Chris Kowal’s Q库启发(https://github.com/kriskowal/q) 。 这个库通过一个回调“promise”让用户监控异步过程。 下面针对使用pormise的语法进行说明。

var promise = callThatRunsInBackground();

promise.then(

  function(answer) {

    // do something

  },

  function(error) {

    // report something

  },

  function(progress) {

   // report progress

  });

返回promise的服务有很多,例如$http, $interval, $timeout。
所有的promise返回一个单例对象, 由我们自己决定它来返回什么。例如$http.get 它的返回对象包含四个键值:data,status, header, config. 这些参数与success回调参数一样,如:

// this
$http.get('/api/v1/movies/avengers')
  .success(function(data, status, headers, config) {
     $scope.movieContent = data;

  });

// is the same as
var promise = $http.get('/api/v1/movies/avengers');

promise.then(

  function(payload) {

    $scope.movieContent = payload.data;

  });

在这个例子中,我们忽略error函数,以及promise的错误,update回调函数。初学者可能会疑惑,为什么我们要这么费力的获取一个答案。但每个人在开始写angluar中non-trivial服务时都会碰壁—–如何传递一个回调函数到一个调用函数,当服务返回早于回调调用前?

Promises and Services

Angular的语法要求我们用promise作为一个callback handle, 在service中进行异步操作,返回一个promise, 当异步工作完成时,然后promise的函数被调用。有了一个思路后,我们建立一个简单的controller和service例子来获取数据,并在页面中显示数据。

angular.module('atTheMoviesApp', [])

  .controller('GetMoviesCtrl', 

    function($log, $scope, movieService) {

     $scope.getMovieListing = function(movie) {

       var promise = 

           movieService.getMovie('avengers');

       promise.then(

          function(payload) { 

              $scope.listingData = payload.data;

          },

          function(errorPayload) {

              $log.error('failure loading movie', errorPayload);

          });

     };

  })

  .factory('movieService', function($http) {

    return {

      getMovie: function(id) {

         return $http.get('/api/v1/movies/' + id);

      }

    }

  });

现在你可能学会调用 异步调用$http方法,获取一个result,并更新用户界面。 服务不会理解UI的含义,你也不能将$scope传递到getMovie函数中, 这样看似很好,然而。。

那怎么进行post操作呢

下载端绑定一个$http.get promise,并将其传递给controller,controller会自己处理这个结果。 如果你想要服务 进行 post-process? 更重要的是,如果你想处理 $http 的error,在服务层,而controller不需要直接处理 404s 等

在更复杂的情况,你可能解决这个问题,通过建立自己的promise,重构service来使用promise,这样就可以在service中处理和拿回我们想要的负载。

...

.factory('movieService', function($http, $log, $q) {

  return {

   getMovie: function(movie) {

     var deferred = $q.defer();

   $http.get('/api/v1/movies/' + movie)

       .success(function(data) { 

          deferred.resolve({

             title: data.title,

             cost: data.price});

       }).error(function(msg, code) {

          deferred.reject(msg);

          $log.error(msg, code);

       });

     return deferred.promise;

   }

  }

 });

更有前途的理由是,我们构建一个内嵌的promise结构,可以控制调用的input和output,合适的log error,转换output,并且提供一个状态来更新 deferred.notify(msg)。

But there is a better, more foolproof way…

但是有一个更好的方法,更简单

Composing Promises 组合promises

我们可以避免创建自己的deferred对象,通过结合一个then转换自己的response,返回一个转换的结果到调用着,来管理分散的promise。例如,我们想包含$http所响应的数据,而不用处理自己的deferred对象:


this.getMovie = function(movie) {

    return $http.get('/api/v1/movies/' + movie)

           .then(

              function (response) {

                return {

                   title: response.data.title,

                   cost:  response.data.price

                });

              });

};

现在这个内容返回到服务方法的then中,将形成一个链promise,它来转换输出。而controller可以做没http的工作


$scope.getMovie = function(movie) {

   service.getMovie(movie) 

   .then(function(movieData) {

      $scope.movieData = movieData;

   });

};

我减少这个代码量,来实现一相同的结果,我们不需要担心在Ajax中将会发生失败的事情。

Handling problems in nested service calls

在service calls 中处理问题

现在我们用一个内嵌的返回 success 函数, promise代码将自动检测error函数的缺失,并终止调用默认的$http的error对象。 但是,你可能会担心如何转换error呢?


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;

              });

};

现在error返回一个单string,不是$http 的error, data、status、 header和config属性。

Doing more than one thing at a time 一次做更多

这是一个强大的service, 当需要同时处理多个异步活动时。 $q功能让我们同时调用多个回调函数,利用单一函数来连接他们。


service('asyncService', function($http, $q) {

      return {

        loadDataFromUrls: function(urls) {

          var deferred = $q.defer();

          var urlCalls = [];

          angular.forEach(urls, function(url) {

            urlCalls.push($http.get(url.url));

          });

          // they may, in fact, all be done, but this

          // executes the callbacks in then, once they are

          // completely finished.

          $q.all(urlCalls)

          .then(

            function(results) {

            deferred.resolve(

             JSON.stringify(results)) 

          },

          function(errors) {

            deferred.reject(errors);

          },

          function(updates) {

            deferred.update(updates);

          });

          return deferred.promise;

        }

      };

    });

这个例子中的负载包含一组对象,Url 如下:


[ 

 { url: 'ajax1.html' },

 { url: 'ajax2.html' },

 { url: 'ajax3.html' }

]

对于每个URL,一个promise 由执行$http.get创建,promise添加到一个数组中,在$q.all 的函数获取一个数组promise, 一个转换结果到一个promise 包含一个对象:


[

  promise1_result_payload,

  promise2_result_payload,

  promise3_result_payload

]

如果这三个调用完成,这个代码将转换为JSON到caller

from:http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值