下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到:
HttpREST.factory('cardResource',function($resource){ return $resource('/card/user/:userID/:id',{userID:123,id:'@id'},{charge:{method:'POST',params:{charge:true},isArray:false}}) }); HttpREST.factory('httpCard',function($q,cardResource){ return { getById:function(cardID){ var defer = $q.defer(); cardResource.get({id:cardID},function(data,headers){ defer.resolve(data); },function(data,headers){ defer.reject(data); }); return defer.promise } } });
$scope.card_1 = httpCard.getById(1);
<span>{{card_1['name']}}</span> <span>{{card_1['amount']}}</span>
这样做的目的很显然,由于后台返回数据需要时间,所以对card_1的赋值应该是异步的,所以getById方法返回的是一个promise,所以,card_1其实也是一个promise,我们把它打印出来可以看到:
1.同步打印:(还不等到后台返回数据就打印)
$scope.card_1 = httpCard.getById(1);
console.log($scope.card_1);
2.异步打印:(等到后台返回数据以后打印)
$scope.card_1 = httpCard.getById(1); $scope.card_1.then(function(){console.log($scope.card_1)});
可以看到,同步打印的card_1,它的$$v是undefined,因为后台还没有返回数据,但异步打印的card_1,它的$$v就是请求后返回的数据.
问题就出现了,card_1只有一个$$v属性和一个then方法,它并没有name属性,也没有amount属性,但是在视图中,<span>{{card_1['name']}}</span>确实渲染了.虽然我没有看过源代码,但是可以推测,视图渲染card_1的时候,是使用了card_1的$$v对象来进行渲染的.所以访问card_1['name'],其实是访问了card_1的$$v['name'].
那么,如果card_1发生变化的时候,又是怎么处理的呢? 我尝试了以下操作:
$scope.updataCard = function(){ $scope.card_1.name='工商银行'; //视图不会发生变化 $scope.card_1.$save() //card_1没有$save方法 };
发现直接操作card_1.name属性,虽然card_1的name属性确实发生了变化,但是,在视图中它并没有任何的变化.可见,视图对于promise对象,监测的依然是它的$$v对象的属性的变化,而它自己的属性变化是没有任何反应的.另外,card_1是promise对象,不是$resource返回的对象,card_1的$$v才是,所以,card_1当然也没有$save方法
那么,如果我要更新card_1,修改card_1,到底应该怎么做的? 说到底,card_1的真身就是card_1的$$v对象,so,想要修改card_1,就要修改它的$$v对象:
$scope.updataCard = function(){ $scope.card_1.$$v.name='工商银行'; $scope.card_1.$$v.$save(); };
这样做,视图就会被更新.但是这样做有一个问题,上面已经看到了,$$v对象是在请求已经得到响应,得到返回的数据的时候才有的,在没有得到响应前,$$v是undefined.所以,如果在还没有得到响应前就执行了updataCard函数,这段代码就会有问题.so,最好的方法是放在promise的then方法的回调函数的参数里:
$scope.updataCard = function(){ $scope.card_1.then(function(data){ data.name='工商银行'; data.$save() }); };
promise对象有一个then方法,then方法了接受三个回调,详细参考:http://www.cnblogs.com/liulangmao/p/3907571.html ,这里只写一个成功的回调,回调的参数data,也就是promise对象的$$v对象,then函数会在响应成功后被调用.所以,即便还没有得到响应就触发了updataCard方法,修改的操作还是会等到响应收到后才执行,这就是异步.
注意,这里的card_1是直接通过$resource返回得到的promise,但如果是通过angular路由的resolve方法返回的对象,在resolve的时候已经取了promise对象的$$v对象,然后再注入到控制器中.这样得到的资源就不再是promise对象了,而已经是promise的$$v对象,后面都正常操作就可以了.