angular $q promise详解
前言
通过本文,你大概能清楚angular promise是个啥,$q又是个啥,以及怎么用它。这里咱们先灌输下promise的思想。
下面写的全是废话,一些看着高逼格其实没什么大作用的概念,想知道$q究竟是什么,怎么去用,建议跳到文章尾部的补充部分,在知道使用后再去补这些较为详细的概念。
一、从promise起步
promise啥意思?打开有道词典=>输入promise=>点击搜索,如图:

OK,一个承诺,许诺,何为许诺?
打完这场仗,我就回老家娶你,flag高高挂起。这就是一个许诺。我告诉你我会娶你,但至于上了战场我可能挂了(响应失败),也许能顺利回来(响应成功),这不是我能确定的,只能先承诺你,你可以在家等我,也可以选择嫁给隔壁老王,我不会在行为上限制你。
其实对于promise的理解,这两天参考了下其它文章,觉得还是父子看天气的一篇举例文章最为精妙,我就直接引用精简分析下,不把它的插画啥的搬过来了,这是知乎翻译,这是英文原文。
父亲需要儿子去后山山坡上通过望远镜观察天气,再判断是否出海捕鱼,儿子出发前许诺(promise)父亲半小时后回来(在这个时间线上这是异步的),而父亲可以在这段期间做自己的事情。那么出现以下几种情况:
情况一:山坡上一眼望去,远方阳光明媚,天气信息获取成功,儿子说OjbK,于是父亲顺利出海捕鱼。承诺兑现,promise=》resolved
情况二:山坡上一眼望去,远方乌云密布,天气信息获取成功,儿子说问题很大,于是父亲决定在家休息。承诺兑现,promise=》resolved
情况三:山坡上云雾缭绕,宛如仙境,一眼望去啥都看不到,天气信息获取失败,父亲觉得有风险,还是在家休息。承兑未兑现,promise=>rejected
那么我们可以将情况一与情况二理解为一次异步的数据请求,都请求到了结果,只是数据得到不同罢了,而情况三则是请求失败,啥数据也没拿到手。
那么我们把上面的故事代码化,这里还是直接借用知乎文代码,稍作注释便于理解。

我们把儿子上山看天气比喻成一个service服务,而父亲会使用这个service服务去获取天气信息,那么先封装service服务。代码如下:
angular.module("myApp",[])
//将获取天气的行为封装为sonService服务
.factory("sonService",function($http,$q){
return {
getWeather: function(){
return $http.get({
method:"GET",//定义http请求方法
url:""//这是你要请求的地址
}).then(function success(resp){
if(resp.data==="good"){
//如果请求的结果OK,那咱们通知父亲出海吧
return resp.data
}else{
//否则别出海了
return $q.reject(resp.data)
}
},function error(resp){
//没请求回来也别出海了,不值得冒险
return $q.reject(resp.data)
})
}
}
})
var makePromiseWithSon = function () {
// 这里咱们开始调用封装的获取天气函数
SonService.getWeather()
.then(function (data) {
// 如果信息获取成功,且是好的
if (data.forecast === 'good') {
//准备出去捕鱼
prepareFishingTrip();
} else {
//否则准备午餐
prepareSundayRoastDinner();
}
}, function (error) {
// 请求失败,还是准备午餐
prepareSundayRoastDinner();
});
};
OK,从灌输promise理念,到模拟了一个小故事,大概说到这里了,在以上代码中,你一定纳闷,$q是啥啊,resolved和rejected又是个啥玩意,没事,咱们下面借着叨叨。
二、从promise谈到$q
如果把promise理解为一种异步编程思想,我们可以把$q看成angular对于这种思想的封装,就是咱们可以直接使用$q来实现异步编程的目的。
我们使用promise的核心目的,还是能及时获得功能组合以及错误冒泡的同时,保持代码异步运行的能力。
重点来了
1.在promise中,只有一个resolve或者reject会被调用,二者只执行其中一个。
resolve被调用时,会带有一个履行值,就是需要返回已请求成功的值。
reject被调用时,要带一个拒绝原因,就是被拒绝后,需要返回请求失败的数据,包含status之类的。
2.如果promise被执行或者拒绝了,依赖于它们的处理程序仍然会被调用。
3.处理程序总是会被异步调用。
那现在开始尝试使用$q吧,首先,我们需要将$q注入到我们想要使用它的对象, 因为angular中已经包含了这个服务,所以就不用额外引入别的js文件了。
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方法去使用它。
本文采用资料:
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,一般做法就是将下单请求写在添加购物车成功的回调中,但是这样代码会显得臃肿,我们还是希望每个功能模块的代码是独立的,这样更便于提升代码的可读性。
angular $q promise详解的更多相关文章
- 量化投资_TB交易开拓者A函数和Q函数详解
//////////////////A函数详解/////////////// //A函数主要在端口上进行下单操作//////////////// A_AccountID说明 返回当前公式应用的交易帐户 ...
- 77.Q表达式详解
Q表达式可以包裹查询条件,可以在多个条件之间进行操作:与或非等.Q表达式一般会放在filter()中进行使用,F表达式一般是放在update()中进行使用. 定义模型的models.py文件中,示例代 ...
- AngularJS 中的Promise --- $q服务详解
先说说什么是Promise,什么是$q吧.Promise是一种异步处理模式,有很多的实现方式,比如著名的Kris Kwal's Q还有JQuery的Deffered. 什么是Promise 以前了解过 ...
- angularjs promise详解
一.什么是Promise Promise是对象,代表了一个函数最终可能的返回值或抛出的异常,就是用来异步处理值的. Promise是一个构造函数,自己身上有all.reject.resolve这几个异 ...
- 关于Promise详解
异步回调 回调地狱 在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的回调地狱 并行结果 如果几个异步操作之间并没有前后顺序之分,但需要等多个异步操作都完成后才能执行后续的任务 ...
- ES6 中 Promise 详解
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Promise 提供统一的 API ...
- ES6中Promise详解
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息. Promise 提供统一的 AP ...
- ES6 Promise 详解
一.概念 Promise,从语法上来讲,它是一个对象,是一个构造函数,可以获取 异步操作 的信息. 简单来讲,就是用同步的方式写异步代码,用来解决回调问题. 二.特点 Promise 对象有两个特点: ...
- js中的promise详解
一 概述 Promise是异步编程的一种解决方案,可以替代传统的解决方案--回调函数和事件.ES6统一了用法,并原生提供了Promise对象.作为对象,Promise有一下两个特点: (1)对象的 ...
随机推荐
- QT中的线程与事件循环理解(2)
1. Qt多线程与Qobject的关系 每一个 Qt 应用程序至少有一个事件循环,就是调用了QCoreApplication::exec()的那个事件循环.不过,QThread也可以开启事件循环.只不 ...
- codeforces891a
A. Pride time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...
- hdu 5074 相邻数和最大dp
http://acm.hdu.edu.cn/showproblem.php?pid=5074 给定一个序列 有些位数未知,给你所有两个数连续所得到的能量,问你怎么安排数字使得总能量最大 二维dp,dp ...
- 初始Hbase
Hbase 定义 HBase是一个开源的非关系型分布式数据库(NoSQL),它参考了谷歌的BigTable建模,实现 的编程语言为 Java. 是Apache软件基金会的Hadoop项目的一部分,运行 ...
- 微擎开启性能优化里面的性能优化memcache内存优化及数据库读写分离
http://www.mitusky.com/forum.php?mod=viewthread&tid=3135 [微擎 安装使用] 微擎开启性能优化里面的性能优化memcache内存优化及数 ...
- WPF学习笔记(5):两个DataGrid的滚动条实现同步滚动
效果:两个DataGrid的滚动条实现同步滚动. 代码参考了博客园chuncn的文章<.net中同步多个ScrollViewer滚动的四种方法>,原文是针对ListBox的.现改为针对Da ...
- 【翻译】 Windows 内核漏洞学习—空指针解引用
Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/windo ...
- [ActionScript 3.0] as3处理xml的功能和遍历节点
as3比as2处理xml的功能增强了N倍,获取或遍历节点非常之方便,类似于json对像的处理方式. XML 的一个强大功能是它能够通过文本字符的线性字符串提供复杂的嵌套数据.将数据加载到 XML 对象 ...
- Nginx 负载均衡和反向代理实践
nginx 以哪个配置文件启动 Nginx 负载均衡和反向代理实践 环境介绍 192.168.1.50 在这台主机上配置Nginx 的反向代理,负载均衡,和web1,web1使用的81号端口 1 ...
- 阿里云RDS数据库备份文件恢复到本地数据库
参考这里:https://help.aliyun.com/knowledge_detail/41817.html 第4.2步要多注释掉一些(应该根据实际报错来注释): [mysqld] innodb_ ...