$q

标签: AngularJS API 中文


-ng模块下的服务 $q官方文档

这是一个能帮助你异步地运行函数,并且在他们仍在进程中的时候使用他们的返回值(或者捕捉异常)的服务

$q的灵感源自Kris Kowal的Q,它实现是一个promises/deferred对象。

$q 可以以下面两种形式来使用 ---
1.类似于Kris Kowal的Q或者jQuery实现的Deferred
2.某种程度上类似于ES6规范中的promises

$q的构造函数

$q可以作为一个构造函数,并可以像流式ES6风格的promise一样使用。它需要一个解析器函数作为第一个参数。这与源自ES6 Harmony 的原生Promise的实现方式是十分相似的。详情见 MDN.
同样也支持构造风格的编程方式,但是目前还没有完全支持ES6 Harmony实现的Promise内的所有方法。

可以被这样使用:

// 此例中,我们假设变量`$ q`和`okToGreet`已经存在于当前作用域
// 他们可以通过依赖注入和传递参数来获取

function asyncGreet(name) { 
  // 执行一些异步操作
  // 适当的时候解析(resolve)或者阻止(reject) Promise. 

  return $q(function(resolve, reject) { 
    
    setTimeout(function() { 
        
      if (okToGreet(name)) { 
        resolve('Hello, ' + name + '!'); 
      } else { 
        reject('Greeting ' + name + ' is not allowed.');
      } 
            
    }, 1000); 
  });
}

var promise = asyncGreet('Robin Hood');

promise.then(function(greeting) { 

  alert('Success: ' + greeting);
    
}, function(reason) { 

  alert('Failed: ' + reason);
  
});

贴士:进度/通知的回调目前还未在ES6风格里得到支持。

不过,更多的传统的CommonJS的风格依然可用,并且记录如下。
The CommonJS Promise 指南 描述 promise 是一个为某个代表着异步操作结果的对象提供互动的接口,而且它的执行也并不依赖于某个特定的时间节点.

从用错误处理解决问题的角度来看 deferredpromise 的一些API是异步编程而 trycatchthrow 关键字是同步编程.

// 此例中,我们假设变量`$ q`和`okToGreet`已经存在于当前作用域
// 他们可以通过依赖注入和传递参数来获取

function asyncGreet(name) {

  var deferred = $q.defer();

  setTimeout(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);
  
});

开始的时候这样做的好处不是十分明显的,我们为什么要增加额外的复杂度?看起来十分麻烦。这样做的好处是源自 promisedeferred API文档确立的,详述请看链接.
此外,promise API 允许构造是因为使用传统的回调形式十分困难。更多相关请参考Q 文档 特别是关于 promise 的串行和并行连接的部分.

Deferred API

一个新的 deferred 实例可以通过调用 $q.defer() 来创建。

这样暴露出了相关 promise 实例,而且API可以用来广播成功或失败的完成,以及任务的状态。

方法

  • resolve(value) – 用 value 来解析返回的 promise. 如果 value 是通过 $q.reject 返回的拒绝构造,那么这个 promise 也将被拒绝.
  • reject(reason) – 以 reason 拒绝返回的 promise. 这等同于由 $q.reject 拒绝构造来解析它。
  • notify(value) - 在promise执行的状态下提供更新. 因此在promise尚未被解析或是拒绝之前会调用很多次.

属性

  • promise – {Promise} – 与 deferred 相关联的 promise 对象.

Promise API

当一个新的 deferred 实例被创建出来,并且可以通过使用 deferred.promise 检索出来,那么实际上一个新的 promise 实例也被创建出来了.
promise 对象的作用是,允许相关部分获得当 deferred 任务完成时的结果。

方法

  • then(successCallback, errorCallback, notifyCallback) – 不管promise 已经被解析(拒绝)还是将要发生这些。一但结果为可知的,then都会调用一个成功或者失败的异步回调函数. 这些回调函数被调用是只会被传递一个单独的参数: 成功结果或是失败被拒绝的原因。此外Additionally, the 在 promise 被解析(拒绝)之前,为了提供了一个进度指示 通知回调(notify)可以被调用0到若干次。
    这个方法返回了一个根据成功 (或失败) 回调函数返回结果来解析的新的 promise
    (除非返回的结果是一个promise, 而这个promise在这种情况下已经由存在于promisepromisevalue 解析)。 依然会通过 notifyCallback 方法的返回值发布通知。promise 不能由这个 notifyCallback 方法解析或者拒绝。

  • catch(errorCallback) - promise.then(null, errorCallback) 的简写

  • finally(callback, notifyCallback) – 虽然允许你观测一个 promise 的实现(拒绝),但并不会修改最终值。 这对于发布资源或者为那些需要做处理的被解析(拒绝)的 promise 做一些清理工作十分有用。

链接 Promise

由于调用promise下的then方法会返回一个源promise, 这使得创建一条promise链变得很容易:

promiseB = promiseA.then(function(result) {
  return result + 1;
});

// 当promiseA被解析之后primiseB会紧接着被解析
// 其值将是promiseA的加1的结果

这有可能会创建一个任意长度的链,因为一个promise可以被另一个promise解析(而这个promise将会被近一步的推迟解析),也有可能在这条链的任意节点暂停/推迟解析。这将 实现像$http的相应拦截器那样强有力的API成为可能.

Kris Kowal's Q 与 $q 之间的区别

以下是他们之间最主要的区别:

  • $q 是通过 $rootScope.Scope 集成的,在Angular中作用域模型观测机制会更快的在你的 model 之间传播解析或拒绝,这避免不必要的浏览器重绘导致的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
  
  deferred.resolve(123);
  
  // 注意! then函数没有被同步调用.
  // 这是因为无论 promise 是被同步或是异步调用的,
  // 我们都希望它的 API 总是异步的
  expect(resolvedValue).toBeUndefined();

  // 使用$apply向 then方法传递 promise 解析.
  $rootScope.$apply();
  expect(resolvedValue).toEqual(123);
  
}));

依赖

$rootScope


用法

$q(resolver);

参数

参数 形式 具体
resolver function(function, function) 函数是为了响应新创建 promise, 函数的第一个参数是用来解析 promise 的函数,函数的第二个参数是用来拒绝 promise 的函数.

返回

Promise - 是新创建出的 promise.

方法

1.defer();

  • 创建一个新的 deferred 对象来表示一个将要在未来完成的任务.

返回

Deferred - 是一个 deferred 的新实例.

2.reject(reason);

  • 创建一个由被指定 reason 的拒绝而解析的新的 promise.它的API将被放到 promise 链的前面,所以如果你正在处理这个 promise 链的最后一个 promise,你并不需要为此感到担心.

当将 deferreds/promises 和更为熟悉的 try/catch/throw 行为相比较时, 把reject当做Javascript里的 throw 关键字. 同样地如果你“捕获catchpromise失败回调返回的error并且你想将此error掷到由当前promise派生出的promise中,你将不得通过借由reject返回的一个拒绝构造来“重抛”这个error.

promiseB = promiseA.then(function(result) {
  // 成功: 执行函数体内部的操作
  //       并用既有或新的结果解析 promiseB
  return result;
  
}, function(reason) {
  // 错误: 尽可能处理错误,
  //       并用 newPromiseOrValue 解析 promiseB,
  //       否则向promiseB转发拒绝
  
  if (canHandle(reason)) {
   // 处理错误并恢复
   return newPromiseOrValue;
  
  }
  return $q.reject(reason);
});

参数

参数 形式 具体
reason * 常量, 信息, 例外或一个代表拒绝原因的对象.

返回

Promise 返回一个由reason拒绝的 promise.

when(value);
把一个 value 或者一个(第三方)可以使用thenpromise 包装到一个 $q promise.这样做的好处是当你再处理一个对象时,你不用再考虑它是不是一个promise,也不必考虑某个外源的 promise 是否是值得信任的.

参数

参数 形式 具体
value * value 或一个 promise

返回

Promise - 返回由一个由验证过的 value 或一个 promise 包裹成的新的 promise.

resolve(value); when的别名,与ES6保持一致.

参数

参数 形式 具体
value * value 或一个 promise

返回

Promise 返回由一个由验证过的 value 或一个 promise 包裹成的新的 promise.

all(promises);

  • 将多个promise合并成一个 promise,它的解析将会在所有输入的promise解析之后执行.

参数

参数 形式 具体
promises Array.<Promise> Object.<Promise> 一个数组或者 promises 的哈希值.

返回

Promise 返回一个单独的 promise,它将被一组数组/哈希 value 解析。在这个单独的 promise 中每一个 value 都会对应到 promise 数组/哈希的索引中. 如果参数中的任何一个 promise 被拒绝,这将使由 all 方法返回的 promise 被同样的理由(上面的拒绝)拒绝掉.

本文由作者原创,翻译内容仍有欠佳之处,请大家多多指正。via 村里有个村长 / @西瓜橘子葡萄冰

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容