angular.module('myApp', [])
.factory('GithubService', function($q) {
// 现在就可以访问到$q库了
注入完成之后,我们就开始使用它,如果我们要使用resolve以及reject还需要调用defer()方法。如下:
var deferred = $q.defer();
而deferred(这个随便你取啥,不限制的)有三个可以使用的方法,以及一个处理promise的promise属性,慢慢道来,先说resolve方法。
defer()方法详解
1.resolve(value):resolve函数一个value来执行deferred promise,表明promise对象由pending状态转为resolved。
deferred.resolve({name: "Ari", username: "@auser"});
2.reject(reason):reject用一个reason来拒绝deferred promise,表明promise对象由pending状态转为rejected。
deferred.reject("Can't update user");
3.notify(value):notify这个方法用于返回一个提醒信息。可在resolve或者reject之前可以被多次调用。
除了三个方法以外,deferred还提供了一个promise属性,比如在我们封装服务最后return deferred.promise,这个属性能让我们去观察原来的promise对象的状态,比如成功了,被拒绝了,但无法修改deferred的内在状态。
上面我们提的是promise的deffered对象,那promise有没有对应的状态监听方法呢,很明显是有点。
promise--then方法。
如果把deffered理解为更改promise状态的方法,那么then就是对应监听promise不同状态的方法。
我们在$http就直接使用过then方法,用于接受处理成功函数以及失败函数,这里我们保持前两者不变,加入了一个未改变状态的监听函数。
.then(successFn, errFn, notifyFn)
划重点,当deffered的resolve将promise状态从pending改为了resolved,直白点,请求成功了,那么我们的then方法里面的第一个回调successFn可以监听到这个状态变化。
当deffered的reject将promise状态从pending改为了rejected,直白点,请求被拒绝了,或者说失败了,那么我们得then方里面的第二个回调errFn可以监听到这个状态变化。
当deffered还啥都没干,还是pedding状态,那么咱们then方法的第三个回调notifyFn就可以监听到。
promise--catch方法
.catch(errFn回调函数)
这个方法稍微好理解点,就只是个快捷方式,能让我们用.catch(function(reason){})取代上面then方法里面的err回调,单独用这个抓响应失败。
promise-finally方法
让你可以观察到一个 promise 是被执行还是被拒绝, 但这样做不用修改最后的 value值。需要注意的是,finally属于JavaScript的保留字,所以你要使用,得这样写:
promise['finally'](function() {});
$q的方法说明
我们在前面说了$q.defer()方法,其实$q除了此方法外还有其它四个方法,下面一一列举。($q.refer()在上面已经提及,这里不再次做说明了)
$q.all(promises)
如果我们想将多个promise合并成一个,可以使用$q.all()来进行合并,它有一个参数promises,promises可以是一个promise数组或者promise的hash。all()方法会返回单个promise,如果其中任意一个promise被拒绝,结果的promise也会被拒绝。
$q.reject(reason)
这个方法会创建一个promise并以你提供的reason去拒绝它。它可以用于在一个promise链中转发拒绝的promise,类似于js中的throw。比如在js中我们可以捕获一个异常,并且抛出这个异常,那么在then链中$q.reject(reason)能帮你实现。
$q.when(value)
when()函数把一个可能是值或者能接着then的promise包装成一个$q promise。这样我们就能处理一个可能是promise也可能不是promise的对象。
wnen中的value是一个值或者是一个promise,但when()会最终返回一个promise,我们也可以正常的用promise方法去使用它。
本文采用资料:
Angular中的$q的形象解释及深入用法
AngularJS 中的Promise --- $q服务详解
2018-6-15补充
准确来说,上面写的算是书籍以及概念的整理,我自己都觉得写的很差,毕竟自己整理完之后,$q使用场景是什么,何时使用,怎么使用还是比较模糊,也是在后续项目问题的解决中慢慢有了个清晰的思路,这里就做个补充。
耐心读完这点点文字,肯定有帮助。
1.$q是用来干嘛的
用来解决异步的,比如我现在有个需求,我要做个订单翻单的功能,就是在个人订单信息中找到已经买过的商品,点击翻单按钮,程序会自动取到这个产品的信息,再次提交到购物车,然后再将此购物车重新下单一次,也就是再购买一次。页面不会跳转,但是整个购物流程会全部跑一遍。
区别在于,我们一般购物操作是先在商品页面选商品,点击添加购物车按钮,没问题我们再点击结算按钮会跳到结算页面,不同的页面不同的点击按钮,这样程序是一步一步点击去执行的,先后顺序也很明确,但现在我这个翻单功能就点击一次按钮就得把整个购物流程跑一遍。
那么问题就来了,买东西需要添加购物车,此时会有个购物车独有ID,下单会依赖这个独有ID去结算这个购物车的商品,逻辑是一次性完成的,而添加购物车请求是异步的,我们怎么知道什么时候添加购物车完成了,可以取购物车的id了,异步问题就在这,状态很难获取。
我原本想的是先定义一个添加购物车函数,并在成功回调中返回添加成功购物车的信息,并利用这个信息去执行我接下来的下单操作,很遗憾,这个信息万年undefined,没法捕捉。难道在添加购物车成功回调中再去定义下单逻辑,那代码多丑陋。
那么我们就得利用$q来帮我们完成了。
2.$q怎么用
我是用$resouce和$q来完成这个需求的,要使用这两个东西,是需要依赖注入$resouce和$q的,这里就是一些基本概念了,假设我们相关依赖注入都做好了。
//假设有个翻单的service叫 reOrderService
//这是我添加购物车的操作
service.addToCart = function(data){
var deferred = $q.defer();//生成deferred异步对象
var url = xxxxx+"api/shoppingCart/items";
var resourcemtd = $resource(url,{},{
add:{//这是$resource对于http请求的方法定义,不用管
method:'POST',
isArray:false,
headers:{
Authorization:userToken
resourcemtd.add(data, function (resp) {
if (resp.success) {;
deferred.resolve(resp);//这个状态无法捕捉,利用$q改变deferred状态为执行成功
}else{
throw new Error('add to cart fail');
return deferred.promise;//返回promise对象
//这是我的控制器操作 调用添加购物车函数,处理promise
var promise = reOrderService.addToCart(data);
promise.then(function(resp){//执行请求成功的回调
var CardId = resp.shoppingCard.id //假设这是di
//假设早service中有个下单函数叫addOrder 调用下单函数,传入购物车id
reOrderService.addOrder(CardId);
因为不知道添加购物车成功回调什么时候才是成功,我们可以利用deferred.resolve()手动将它改成成功状态,同时返回一个了一个promise对象。
promise.then()属于promise对象的一个回调方法,第一个函数执行异步成功的函数,比如我们在第一个回调中去拿到添加购物车返回的数据,然后去调用下单操作。
如果我们不用$q,一般做法就是将下单请求写在添加购物车成功的回调中,但是这样代码会显得臃肿,我们还是希望每个功能模块的代码是独立的,这样更便于提升代码的可读性。