$q -- AngularJS中的服务(理解)
描述
译者注: 看到了一篇非常好的文章,如果你有兴趣,可以查看: Promises与Javascript异步编程 , 里面对Promises规范和使用情景,好处讲的非常好透彻,个人觉得简单易懂。
既然是用来处理异步编程的,那么在浏览器端的JS里,主要是2种: setTimeout 和 Ajax 请求. promise 的使用就很像Ajax请求的成功和失败回调。
此承诺/延迟(promise/deferred)实现的灵感来自于 Kris Kowal's Q CommonJS Promise建议文档将承诺(promise) 作为和 异步执行操作(action)结果对象进行交互的接口,在指定的时间内可能完成也可能不能够完成(如超时,错误,拦截等等)。
从错误处理的角度看,延迟(deferred )和承诺(promise ) API 对于异步编程来说, 和同步编程的 try,catch, 以及throw作用差不多.
- // 为了演示的目的,此处我们假设 `$q`, `scope` 以及 `okToGreet` 引用 在当前执行环境中可用
- // (比如他们已经被注入,或者被当做参数传进来了).
- function asyncGreet(name) {
- var deferred = $q.defer();
- setTimeout(function() {
- // 因为此function 在未来的事件循环中异步执行,
- // 我们需要把代码包装到到一个 $apply 调用中,以便正确的观察到 model 的改变
- scope.$apply(function() {
- deferred.notify('即将问候 ' + name + '.');
- if (okToGreet(name)) {
- deferred.resolve('你好, ' + name + '!');
- } else {
- deferred.reject('拒绝问候 ' + name + ' .');
- }
- });
- }, 1000);
- return deferred.promise;
- }
- var promise = asyncGreet('小漠漠');
- promise.then(function(greeting) {
- alert('成功: ' + greeting);
- }, function(reason) {
- alert('失败鸟: ' + reason);
- }, function(update) {
- alert('收到通知: ' + update);
- });
引人这种额外的复杂性的效果 在起初可能不明显。 在 promise 和 deferred APIs 进行承诺时好处就看出来了,请参考: https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md
另外 promise api允许那些在传统的回调( CPS )方法中很难的实现的组合。 更多信息请查阅 Q文档 ,特别是 串行与并行的合并一节。
延迟接口 | Deferred API
通过调用 $q.defer() 可以构建一个新的 deffered 实例。
deffered 对象用来将 Promise 实例与 标记任务状态(执行成功还是不成功)的 API 相关联。
deffered 对象的方法
- resolve(value) ——传入 value 解决派生的 promise。 如果 value 是一个通过 $q.reject 构造的拒绝对象(rejection) , 该promise 将被拒绝。
- reject(reason) ——拒绝派生的promise,并提供原因 。 这相当于通过 $q.reject构造的拒绝对象(rejection)作为参数传递给 resolve。
- notify(value) ——在 promise 执行的过程中提供状态更新。 这在 promise 被解决或拒绝之前可能会被多次调用。
deffered 对象的属性
promise – {Promise} —— 与延迟(deferred)相关联的 promise 对象。
承诺 接口 | Promise API
当创建 deferred 实例时会创建一个新的 promise 对象,并可以通过 deferred.promise 得到该引用。
promise 对象的目的是在 deferred 任务完成时,允许感兴趣的部分取得其执行结果。
promise 对象的方法
- then(successCallback, errorCallback, notifyCallback) ——不管 promise 是被处理还是被拒绝, 一旦结果可用,then 就会尽快地异步调用 成功/错误 回调函数 只要结果是可用的。 调用回调函数时传递单个参数: 结果 或拒绝的理由。 此外,notify 回调可能被调用 0到多次,以提供 提供一个进度指示,之前承诺解决或拒绝。
这个方法 返回一个新的promise 对象, 根据 successCallback , errorCallback的返回值进行解决或拒绝 。 它还通过 notifyCallback 方法的返回值进行通知。 promise 不能从notifyCallback方法得到解决或拒绝 。
- catch(errorCallback) —— promise.then(null, errorCallback) 的快捷方式
- finally(callback) ——让你可以观察到一个 promise 是被执行还是被拒绝, 但这样做不用修改最后的 value值。 这可以用来做一些释放资源或者清理无用对象的工作,不管promise 被拒绝还是解决。 更多的信息请参阅完整文档规范.
因为在 ES3版本的JavaScript中 finally 是一个保留字关键字,不能作为属性名,为了适配 IE8,您需要使用 promise['finally'](callback) 这种形式来调用该方法。
promise 链 | Chaining promises
因为调用一个 promise 的 then 方法返回一个新的派生 promise实例,所以构建promises链也是很容易的:
- promiseB = promiseA.then(function(result) {
- return result + 1;
- });
- // promiseB 将会在处理完 promiseA 之后立刻被处理,
- // 并且其 value值是promiseA的结果增加1
我们可以创建任意长度的promise链;因为一个promise可以被另一个promises处理(进一步推迟解决完成时间),所以在promise链上的任意一点进行 暂停/推迟解决 都是可行的。 这使得实现功能强大的APIs 成为现实,例如 $http 的响应拦截器。
Kris Kowal's Q 与 $q 之间的区别
主要区别有两点:
- Angular中的$q 集成了 ng.$rootScope.Scope Scope模型观察机制,这意味着对models 的解决或拒绝速度将会更快,避免不必要的浏览器重绘(会导致UI闪烁)。
- Q 比 $q拥有更多的功能特性,但带来的是代码字节数的增加。 $q 很轻量级,但包含了一般异步任务所需的所有重要功能。
测试
- it('should simulate promise', inject(function($q, $rootScope) {
- var deferred = $q.defer();
- var promise = deferred.promise;
- var resolvedValue;
- promise.then(function(value) { resolvedValue = value; });
- expect(resolvedValue).toBeUndefined();
- // 模拟 promise 的 resolving
- deferred.resolve(123);
- // 注意 'then' function 不是同步调用的.
- // 因为我们想要 promise API 一直是异步的(async),
- // 不管是在同步调用还是异步调用中都是如此.
- expect(resolvedValue).toBeUndefined();
- // 使用 $apply()将 promise resolution 传递到 'then' functions .
- $rootScope.$apply();
- expect(resolvedValue).toEqual(123);
- }));
依赖关系 | Dependencies
方法 | Methods
all(promises)
结合多个promises为单个promise,在所有输入的promise都处理之后,组合之后的promise才会处理完成。
- 参数: promises
- 类型: Array.<Promise>/Object.<Promise>
- 描述: promises的数组或者引用
- 返回: Promise 返回单个的 promise,将与一个数组解决/散列值, 每个值对应于在相同的索引/关键的承诺 承诺 /散列数组。 如果任何承诺解决排斥,这产生的承诺将被拒绝 拒绝相同的值。
defer()
创建一个 递延 对象代表一个将来完成任务。
- 返回 Deferred 返回一个新实例的Deferred。
reject(reason)
创建一个指定拒绝原因的promise. 此api应该用于在一个promises链中进行拒绝。 如果你正在处理promise 链中的最后一个promise,你不需要担心。
把 deferreds/promises 与我们熟悉的的 try/catch/throw行为进行对比,可以认为 reject 相当于 JavaScript 中的throw 关键字。 这也意味着如果你通过一个 promise 的 error回调, “catch”了一个错误 ,你想要指明当前的承诺已经执行出错了,就必须重新抛出一个“附带了错误信息,拒绝通过的reject” 。
- promiseB = promiseA.then(function(result) {
- // success: 此处可以执行某些操作,然后直接使用原有的result,
- // 或者对result进行操作,来处理接下来的promiseB
- return result;
- }, function(reason) {
- // error: handle the error if possible and
- // resolve promiseB with newPromiseOrValue,
- // 否则转向拒绝 promiseB 的分支
- if (canHandle(reason)) {
- // 处理错误和恢复
- return newPromiseOrValue;
- }
- return $q.reject(reason);
- });
- 参数: reason
- 类型: *
- 描述: Constant, message, exception 或代表拒绝原因的 object。
- 返回: Promise 返回一个promise ,已经因为 reason 而被拒绝了 。
when(value)
将一个对象(可能是value 或 [第三方]then-able promise) 包装为一个 $q promise。 这在你不确定所处理的对象是否是一个promise 时是很有用的,有可能该对象来自于一个不被信任的源头。
- 参数: value
- 类型: *
- 描述: promise 的值
- 返回 Promise 根据传入的值/或promise 返回一个包装后的 promise
随机推荐
- [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the tw ...
- BZOJ 3237: [Ahoi2013]连通图
3237: [Ahoi2013]连通图 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1161 Solved: 399[Submit][Status ...
- ASP.NET Core MVC 在linux上的创建及发布
前言 ASP.NET core转眼都发布半月多了,社区最近也是非常活跃,虽然最近从事python工作,但也一直对.NET念念不忘,看过了园区大神们搭建的Asp.net core项目之后,自己也是跃跃欲 ...
- [转] JAVA读取excel数据(插入oracle数据库)
原文地址:http://blog.csdn.net/zczzsq/article/details/16803349 本实例做的是读取execl(只能读取.xls的execl,即只能读取03版的),如果 ...
- Gson将字符串转换成JsonObject和JsonArray
以下均利用Gson来处理: 1.将bean转换成Json字符串: public static String beanToJSONString(Object bean) { return new Gso ...
- listbox 多选删除(找了好多都不行,终于让我写出来了)
方法一:两个for循环 aspx.cs private string id=""; protected void btn_del_Click(object sender, Even ...
- C#检验参数合法性公用方法
#region 检验参数合法性,数值类型不小于0,引用类型不能为null,否则抛出异常 /// <summary> /// 检验参数合法性,数值类型不小于0,引用类型不能为null,否则抛 ...
- hibernate主键生成策略
在hibernate中,提供了多种主键生成器(不同的数据库,不同的表结构使用的主键生成策略也不相同),查阅相关资料经过实验总结如下: 1.increment 主键按照数值顺序递增,使用当前实例中最大值 ...
- 封装js的部分兼容性
//获取标签的内容(兼容所有浏览器)function getInnerText(element) { //能力检测(先判断如果这个能力有这个) if(typeof element.innerText ...
- 关于push数组,然后遍历数组遇到的坑,遍历显示函数
我偷了个懒将点击的东西push进一个arr里,然后遍历显示在上面. 为啥子出现了上函数,什么鬼什么鬼.我检查很久都不晓得那里push进去的. 一个小时后,我想想要不看看arr里面的结构吧! 尼玛!为啥 ...