Angular中的$q的形象解释及深入用法
作者:寸志
链接:https://zhuanlan.zhihu.com/p/19622332
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
早上,老爸说:“儿子,天气如何?”
每周一早上,老爸问儿子下午的天气情况,儿子可以到自家房子旁边小山上使用望远镜来观看。儿子在出发时许诺(Promise)老爸(会通知老爸天气情况)。
此刻,老爸决定,如果天气不错,明天就出去捕鱼,否则就不去。而且如果儿子无法获得天气预报的话,也不去捕鱼。
30分钟左右,儿子回来了,每周的结局都不一样。
结局A:成功获得了(retrieved)天气预报,晴天 :)
儿子成功获取了天气预报,天空晴朗,阳光明媚!承诺(Promise)兑现了(resolved),于是老爸决定开始为周日的捕鱼做准备。
结局B:同样成功获得了天气预报,雨天:(
儿子成功获得了天气预报,只不过是乌云密布,要下雨。承诺(Promise)兑现了(resolved),只是老爸决定呆在家里,因为天气很糟糕。
结局C:没法获得天气预报:-/
出了问题,儿子没法得知天气预报,因为雾很大,就算站在小山上也无法看清。儿子没办法对象他离开时许下的诺言, promise was rejected!老爸决定留下来,这并不值得冒险。
从编程的角度来看
在这种情况下,老爸是逻辑控制,他把儿子当做service来处理。
这里面的逻辑我们已经知道了,老爸要儿子给他预报天气,但是儿子没法马上告诉他,于是许诺他会带着天气预报回来,老爸在儿子回来之前有其他的事情要做。当老爸从儿子那获得天气预报之后,再决定是打包出发还是呆在家里。这个故事的重点是,儿子去山顶上看天气这件事并没有妨碍(block)老爸干其他的事情。因此对于这种情况,最好的方式就是许诺,随后再兑现或者不兑现。
我们可是使用Angular的then()指定老爸针对每种结果的对策。then()函数接受两个函数作为参数:一个许诺对现时执行,一个在无法对现时执行。
Controller:FatherCtrl
老爸控制情况:
- // function somewhere in father-controller.js
- var makePromiseWithSon = function () {
- // This service's function returns a promise, but we'll deal with that shortly
- SonService.getWeather()
- // then() called when son gets back
- .then(function (data) {
- // promise fulfilled
- if (data.forecast === 'good') {
- prepareFishingTrip();
- } else {
- prepareSundayRoastDinner();
- }
- }, function (error) {
- // promise rejected, could log the error with: console.log('error', error);
- prepareSundayRoastDinner();
- });
- };
Service:SonService
儿子就是一个服务,他爬上小山,尝试预报天气。我们可以把儿子通过望远镜查看即将到来的天气,假设成条用一个天气的API,通常是异步的。要么获得答案,要么出现问题(比方说返回500,大雾皑皑的天空)。
“捕鱼天气API”通过一个许诺返回结果,如果兑现的话,结果的格式像 { "forecast": "good" }这样。
- app.factory('SonService', function ($http, $q) {
- return {
- getWeather: function () {
- // the $http API is based on the deferred/promise APIs exposed by the $q service
- // so it returns a promise for us by default
- return $http.get('http://fishing-weather-api.com/sunday/afternoon')
- .then(function (response) {
- if (typeof response.data === 'object') {
- return response.data;
- } else {
- // invalid response
- return $q.reject(response.data);
- }
- }, function (response) {
- // something went wrong
- return $q.reject(response.data);
- });
- }
- };
- });
总之
老爸让儿子预报的天气就是一个比喻,说明了异步的本质。老爸并不想在门口傻等着儿子回来,他还有别的事情要做。而是在门口许个诺言,决定在3种不同的情况(天气好/坏/不知道)下该怎么办,儿子离开时就许下一个诺言,在他回来的时候会兑现或者食言。
儿子需要处理一个异步的API(通过望远镜来查看天空/使用天气API)来获得天气,但这些对他老爸来说都是未知的,是谁不懂科技!?
原文:Promises in AngularJS, Explained as a Cartoon
扩展: 看完之后对$q的用法想深入了,接着看下面,
在用JQuery的时候就知道 promise 是 Js异步编程模式的一种模式,但是不是很明白他跟JQuery的deferred对象有什么区别。随着公司项目的进行,要跟后台接数据了,所以决定搞定它。
Promise
Promise是一种模式,以同步操作的流程形式来操作异步事件,避免了层层嵌套,可以链式操作异步事件。
我们知道,在编写javascript异步代码时,callback是最最简单的机制,可是用这种机制的话必须牺牲控制流、异常处理和函数语义化为代价,甚至会让我们掉进出现callback大坑,而promise解决了这个问题。
ES6中Promise、angularJS内置的AngularJS内置Q,以及when采用的都是Promises/A规范,如下:
每个任务都有三种状态:未完成(pending)、完成(fulfilled)、失败(rejected)。
1.pending状态:可以过渡到履行或拒绝状态。
2.fulfilled状态:不能变为其他任何状态,而且状态不能改变,必须有value值。
3.rejected状态:不能变为其他任何状态,而且状态不能改变,必须有reason。
状态的转移是一次性的,状态一旦变成fulfilled(已完成)或者failed(失败/拒绝),就不能再变了。
return name === 'Robin Hood';
}
function asyncGreet(name) {
var deferred = $q.defer();
setTimeout(function() {
// 因为这个异步函数fn在未来的异步执行,我们把代码包装到 $apply 调用中,一边正确的观察到 model 的改变
$scope.$apply(function() {
deferred.notify('About to greet ' + name + '.');
if (okToGreet(name)) {
deferred.resolve('Hello, ' + name + '!');
} else {
deferred.reject('Greeting ' + name + ' is not allowed.');
}
});
}, 1000);
return deferred.promise;
}
var promise = asyncGreet('Robin Hood');
promise.then(function(greeting) {
alert('Success: ' + greeting);
}, function(reason) {
alert('Failed: ' + reason);
}, function(update) {
alert('Got notification: ' + update);
});
Q Promise的基本用法
上面代码表示, $q.defer() 构建的 deffered 实例的几个方法的作用。如果异步操作成功,则用resolve方法将Promise对象的状态变为“成功”(即从pending变为resolved);如果异步操作失败,则用reject方法将状态变为“失败”(即从pending变为rejected)。最后返回 deferred.promise ,我们就可以链式调用then方法。
JS将要有原生Promise,ES6中已经有Promise对象,firefox和Chrome 32 beta版本已经实现了基本的Promise API
AngularJs中的$q.defferd
通过 调用 $q.defferd 返回deffered对象以链式调用。该对象将Promises/A规范中的三个任务状态通过API关联。
deffered API
deffered 对象的方法
1.resolve(value):在声明resolve()处,表明promise对象由pending状态转变为resolve。
2.reject(reason):在声明resolve()处,表明promise对象由pending状态转变为rejected。
3.notify(value) :在声明notify()处,表明promise对象unfulfilled状态,在resolve或reject之前可以被多次调用。
deffered 对象属性
promise :最后返回的是一个新的deferred对象 promise 属性,而不是原来的deferred对象。这个新的Promise对象只能观察原来Promise对象的状态,而无法修改deferred对象的内在状态可以防止任务状态被外部修改。
Promise API
当创建 deferred 实例时会创建一个新的 promise 对象,并可以通过 deferred.promise 得到该引用。
promise 对象的目的是在 deferred 任务完成时,允许感兴趣的部分取得其执行结果。
promise 对象的方法
1.then(errorHandler, fulfilledHandler, progressHandler):then方法用来监听一个Promise的不同状态。errorHandler监听failed状态,fulfilledHandler监听fulfilled状态,progressHandler监听unfulfilled(未完成)状态。此外,notify 回调可能被调用 0到多次,提供一个进度指示在解决或拒绝(resolve和rejected)之前。
2.catch(errorCallback) —— promise.then(null, errorCallback) 的快捷方式
3.finally(callback) ——让你可以观察到一个 promise 是被执行还是被拒绝, 但这样做不用修改最后的 value值。 这可以用来做一些释放资源或者清理无用对象的工作,不管promise 被拒绝还是解决。 更多的信息请参阅 完整文档规范.
通过then()方法可以实现promise链式调用。
return result + 1;
});
// promiseB 将会在处理完 promiseA 之后立刻被处理,
// 并且其 value值是promiseA的结果增加1
$q的其他方法
$q.when(value):传递变量值,promise.then()执行成功回调
$q.all(promises):多个promise必须执行成功,才能执行成功回调,传递值为数组或哈希值,数组中每个值为与Index对应的promise对象
再深入点:
1. $q
$q是Angular的一种内置服务,它可以使你异步地执行函数,并且当函数执行完成时它允许你使用函数的返回值(或异常)。
2. defer
defer的字面意思是延迟,$q.defer() 可以创建一个deferred实例(延迟对象实例)。
deferred 实例旨在暴露派生的Promise 实例,以及被用来作为成功完成或未成功完成的信号API,以及当前任务的状态。这听起来好复杂的样子,总结$q, defer, promise三者之间的关系如下所示。
- var deferred = $q.defer(); //通过$q服务注册一个延迟对象 deferred
- var promise = deferred.promise; //通过deferred延迟对象,可以得到一个承诺promise,而promise会返回当前任务的完成结果
defer的方法:
1. deferred.resolve(value) 成功解决(resolve)了其派生的promise。参数value将来会被用作promise.then(successCallback(value){...}, errorCallback(reason){...}, notifyCallback(notify){...})中successCallback函数的参数。
2. deferred.reject(reason) 未成功解决其派生的promise。参数reason被用来说明未成功的原因。此时deferred实例的promise对象将会捕获一个任务未成功执行的错误,promise.catch(errorCallback(reason){...})。补充一点,promise.catch(errorCallback)实际上就是promise.then(null, errorCallback)的简写。
3. notify(value) 更新promise的执行状态(翻译的不好,原话是provides updates on the status of the promise's execution)
defer的小例子:

- function asyncGreet(name) {
- var deferred = $q.defer(); //通过$q.defer()创建一个deferred延迟对象,在创建一个deferred实例时,也会创建出来一个派生的promise对象,使用deferred.promise就可以检索到派生的promise。
- deferred.notify('About to greet ' + name + '.'); //延迟对象的notify方法。
- if (okToGreet(name)) {
- deferred.resolve('Hello, ' + name + '!'); //任务被成功执行
- } else {
- deferred.reject('Greeting ' + name + ' is not allowed.'); //任务未被成功执行
- }
- return deferred.promise; //返回deferred实例的promise对象
- }
- function okToGreet(name) {
- //只是mock数据,实际情况将根据相关业务实现代码
- if(name == 'Superman') return true;
- else return false;
- }
- var promise = asyncGreet('Superman'); //获得promise对象
- //promise对象的then函数会获得当前任务也就是当前deferred延迟实例的执行状态。它的三个回调函数分别会在resolve(), reject() 和notify()时被执行
- promise.then(function(greeting) {
- alert('Success: ' + greeting);
- }, function(reason) {
- alert('Failed: ' + reason);
- }, function(update) {
- alert('Got notification: ' + update);
- });

3. promise
当创建一个deferred实例时,promise实例也会被创建。通过deferred.promise就可以检索到deferred派生的promise。
promise的目的是允许interested parties 访问deferred任务完成的结果。
按照CommonJS的约定,promise是一个与对象交互的接口,表示一个动作(action)的结果是异步的,而且在任何给定的时间点上可能或不可能完成。(这句话好绕口,我的理解是promise相当于一个承诺,承诺你这个任务在给定的时间点上可能会完成,也可能完成不了。如果完成了那就相当于resolve, 如果未完成就相当于reject。不知道这样理解对不对?)
promise 的方法:
1. then(successCallback, errorCallback, nitifyCallback) 根据promise被resolve/reject,或将要被resolve/reject,调用successCallback/errorCallback。
2. catch(errorCallback) then(null, errorCallback)的缩写。
3. finally(callback, notifyCallback)
补充说明:
promise.then()会返回一个新的衍生promise,形成promise链。例如:
- promiseB = promiseA.then(function(result) {
- return result + 1;
- });
- // promiseB will be resolved immediately after promiseA is resolved and its value
- // will be the result of promiseA incremented by 1
Angular中的$q的形象解释及深入用法的更多相关文章
- 形象的讲解angular中的$q与promise(转)
以下内容摘自http://www.ngnice.com/posts/126ee9cf6ddb68 promise不是angular首创的,作为一种编程模式,它出现在……1976年,比js还要古老得多. ...
- 原创:形象的讲解angular中的$q与promise
promise不是angular首创的,作为一种编程模式,它出现在……1976年,比js还要古老得多.promise全称是 Futures and promises.具体的可以参见 http://en ...
- angular中的$q.defer()服务异步处理
jquery和angular都有defer服务,我暂以angular为例谈谈我的理解,最后并附上jquery的阮一峰总结的defer. 以我目前项目的部分代码为例说明为什么要用deferred. fu ...
- promise和Angular中的 $q, defer
在ES6语法中,新出了promise构造函数, 可用来生成promise实例. Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作).有了promise对象, 可以将异步操作以同步 ...
- angular中的$q服务实例
用于理解$q服务 参考:http://www.zouyesheng.com/angular.html#toc39 广义回调管理 和其它框架一样, ng 提供了广义的异步回调管理的机制. $http 服 ...
- angular中的$q服务
$q的一共有四个api: 1.$q.when(value, successFn, errorFn, progressFn),返回值为一个promise对象 --value可以是一个任意数据,也可以是一 ...
- 函数编程中functor和monad的形象解释
函数编程中functor和monad的形象解释 函数编程中Functor函子与Monad是比较难理解的概念,本文使用了形象的图片方式解释了这两个概念,容易理解与学习,分别使用Haskell和Swift ...
- Deferred在jQuery和Angular中的使用与简单实现
Deferred在jQuery和Angular中的使用与简单实现 Deferred是在jQuery1.5版本中加入的,并且jQuery使用它完全重写了AJax,以前也只是偶尔使用.但是上次在使用Ang ...
- angular源码分析:angular中脏活累活承担者之$parse
我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...
随机推荐
- Ruby for Sketchup 贪吃蛇演示源码(naive_snake)
sketchup是非常简单易用的三维建模软件,可以利用ruby 做二次开发, api文档 http://www.rbc321.cn/api 今天在su中做了一款小游戏 贪吃蛇,说一下步骤 展示 主要思 ...
- 微信小程序支付C#后端源码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...
- Heap-451. Sort Characters By Frequency
Given a string, sort it in decreasing order based on the frequency of characters. Example 1: Input: ...
- tomcat JNDI Resource 配置
最近公司的项目慢慢开始向Maven项目迁移, 部分配置文件公共组也帮我们做了些改动,其中在spring的applicationContext.xml中看到了数据连接bean存在两个,一个是jndi 一 ...
- [JS深入学习]——数组对象排序
(转) JavaScript实现多维数组.对象数组排序,其实用的就是原生的sort()方法,用于对数组的元素进行排序. sort() 方法用于对数组的元素进行排序.语法如下: arrayObject. ...
- Android之自定义控件
开发自定义控件的步骤: 1.了解View的工作原理 2. 编写继承自View的子类 3. 为自定义View类增加属性 4. 绘制控件 5. 响应用户消息 6 .自定义回调函数 一.Vie ...
- 初识PHP之php运行流程及原理(一)
初识PHP一.用脚本命令行运行php(1)打开cmd.exe(winkey+R)(2)找到php.exe(拖进cmd即可)(3)输入命令php.exe -f "文件实际路径"注:运 ...
- Python Django 的学习资料
十分有用的链接: 链接1:http://www.cnblogs.com/wupeiqi/articles/5433893.html (银角大王) 链接2:https://www.cnblogs.c ...
- ubuntu 镜像站部署
定时任务 #mirror web */5 * * * * cd /mirror && git pull #mysql mirror 0 */3 * * * rsync -av --de ...
- C++ 定时器Timer在项目中的使用
目录 1.情况说明 2.空循环实现 3.定时器实现 1.情况说明 由于最近要在项目里做弹出弹幕,要求是弹出1秒后消失,一开始我使用空循环进行计时,发现执行到这段代码CPU占用率上升十几个百分点,后来改 ...