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)对象的 ...
随机推荐
- 防Xss注入
转自博客:https://blog.csdn.net/qq_21956483/article/details/54377947 1.什么是XSS攻击 XSS又称为CSS(Cross SiteScrip ...
- bootstrap4.2 导航搜索框
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- RxSwift学习笔记5:Binder
使用 Binder 创建观察者 //Observable序列(每隔1秒钟发出一个索引数) let scheduleObservable = Observable<Int>.interval ...
- [译]C#和.NET中的字符串
原文地址:Jon Skeet:Strings in C# and .NET System.String 类型(在C#语言中对应的别名是string)是.NET最重要的类型之一,不幸的是在它身上存在了太 ...
- [转载]DevOps发展的四个重要阶段
DevOps是敏捷开发的延续,它将敏捷的精神延伸至IT运营(IT Operation)阶段.敏捷开发的主要目的是响应变化,快速交付价值.以2001年的敏捷宣言发布这个里程碑为起点,开始几年内企业主要在 ...
- 新建WebAPI项目时遇到的问题
1 处理程序“ExtensionlessUrlHandler-Integrated-4.0”在其模块列表中有一个错误模块“ManagedPipelineHandler” 以管理员运行下面的命令注册 ...
- 关于Entity Framework的概念及搭建
什么是EF? ADO.NET Entity Framework 是一个对象-关系的映射架构. 它支持直接定义完全独立于数据库结构的实体类,并把它们映射到数据库的表和关系上. 三种编程模型: 数据库优先 ...
- react-native项目之样式总结
react native(以下简称rn)里面涉及到的样式规则都是在js文件中写的,一改pc端的在样式文件中定义规则,所以pc端开发习惯的童鞋转向开发rn项目时,可能会对样式感到有些奇怪:其实react ...
- C#实时检测端口占用情况
在TCP/IP协议中,服务端需要去监听客户端的端口,开始监听,我们需要检测使用的端口是否被占用,获取系统当前使用的所有端口号,用此端口进行匹配即可. 代码如下 internal static Bool ...
- lua编程之lua与C相互调用
lua是扩展性非常良好的语言,虽然核心非常精简,但是用户可以依靠lua库来实现大部分工作.除此之外,lua还可以通过与C函数相互调用来扩展程序功能.在C中嵌入lua脚本既可以让用户在不重新编译代码的情 ...