1

最近在开发项目的时候。我在service中请求数据返回给控制器的时候,由于数据是异步请求的,这里需要知道javascript的运行环境是单线程的,一次只能执行一个任务,但是单线程坏处就是如果前一个任务执行时间较长就会导致整个页面的阻塞,因此javascript提供了异步请求,使任务可以不用等待上一个任务执行完成。但就是这个异步的请求,导致我的data数据还没返回就已经return数据到控制层了,从而返回值为空。

goodsService

app.factory('goodsService', function($http,$q) {
    var goodsData=111;
    function get() {
        $http({
            method:'get',
            url:'test/goods.json'
        }).success(function(data,status,headers,config){
            console.log('get success...');
            console.log(goodsData);
            goodsData=data;
           return goodsData;

        }).error(function(data,status,headers,config){
            console.log('get error...');
        })
    }
    function set(data) {
        $http({
            method:'post',
            url:'test/goods.json'
        }).success(function(data,status,headers,config){
            goodsData=data;
            console.log('get success...');
            console.log(data);
        }).error(function(data,status,headers,config){
            console.log('get error...');
        })
    }
    return {
        set: set,
        get: get
    }
});

controller.js

$scope.goods=goodsService.get();
console.log('scope.goods='+$scope.goods);

图片描述

当然很多人都有很多种解决办法。比如说函数引用:

$.get(/test/goods.json', function(data) {
  return data;
});

当然都是可行的。但是当处理比较复杂的多个异步进行的时候那就真是看的头晕眼花。
因此Promise应运而生。

Promise的身份之谜

Promise有两部分:

  • Deferred定义工作单元,
    用来定义工作单元的开始,处理和结束三部分

  • Promise接收Deferred返回的数据。
    有状态和句柄。Promise 不同于回调的很重要的一个点是,你可以在 Promise 状态变成执行(resolved)之后追加处理句柄。这就允许你传输数据,而忽略它是否已经被应用获取,然后缓存它,等等之类的操作,因此你可以对数据执行操作,而不管它是否已经或者即将可用。

在Angularjs使用Promise的时候需要用到内置服务$q。

  • $q服务受到Kris Kowal的Q库的启发,所以类似于那个库,但是并没有包含那个库的所用功能。

  • $q是跟AngularJS的$rootScope模板集成的,所以在AngularJS中执行和拒绝都很快。 $q

  • promise是跟AngularJS模板引擎集成的,这意味着在视图中找到任何Promise都会在视图中被执行或者拒绝。
    $q实现了上面提到的所有的Deferred和Promise方法。

$q.defer()提供给我们一个创建Deferred对象的方法。这个Deffered对象有个promise属性,这个属性带有6个方法:

  • resolve(value):用来执行deferred promise,value可以为字符串,对象等。

  • reject(value):用来拒绝deferred promise,value可以为字符串,对象等。

  • notify(value):获取deferred promise的执行状态,然后使用这个函数来传递它。

  • then(successFunc, errorFunc,notifyFunc):无论promise是成功了还是失败了,当结果可用之后,then都会立刻异步调用successFunc,或者'errorFunc',在promise被执行或者拒绝之前,notifyFunc可能会被调用0到多次,以提供过程状态的提示。

  • catch(errorFunc)

  • finally(callback)
    更加形象点。就比如说我现在遇到的这个问题。当我的goodsService请求商品数据的时候,我先用$q.defer()创建了一个deferred对象。然后通过该对象的promise属性获取到一个promise对象。这时我进行数据请求,定义请求成功和请求失败的计划分别是deferred.resolve(data)和deferred.reject()。然后将这个promise返回给请求service的控制层。控制层通过.then创建一个执行链,它允许我们中断基于更多功能的应用流程,可以借此导向不同的的结果,再来进行不同的操作。

goodsService

app.factory('goodsService', function($http,$q) {
    function get() {
        var deferred=$q.defer();
        var promise=deferred.promise;
        $http({
            method:'get',
            url:'test/goods.json'
        }).success(function(data,status,headers,config){
            deferred.resolve(data);//执行成功
        }).error(function(data,status,headers,config){
            deferred.reject();//执行失败
        })
        console.log('return promise');
        return promise;
    }
    function set(data) {
        $http({
            method:'post',
            url:'test/goods.json'
        }).success(function(data,status,headers,config){
            deferred.resolve(data);//执行成功
            console.log('get success...');
        }).error(function(data,status,headers,config){
            console.log('get error...');
        })
    }
    return {
        set: set,
        get: get
    }
});

productListCtrl

goodsService.get().then(function(result){
    console.log('goodsService get successed');
    $scope.goods=result;
    console.log('scope.goods1='+$scope.goods);
    },function(){
    console.log('goodsService get error');
    });
    console.log('scope.goods2='+$scope.goods);
});

图片描述

数据显示正常。也可以从下面的scope.goods1和scope.goods2看出promise的特性,在等待返回选择不同计划的时候不会阻塞其他的任务执行。


黑阔大人
278 声望33 粉丝

在这群星闪耀之年 我的征途是星辰大海