anglarjs翻译: $q

最近对英语文档的阅读理解遇到了一个瓶颈,感觉需要n遍重复的琢磨,受到公司同事的启发,尝试翻译下,也帮助和我有同样问题的小伙伴。

1. What’s $q

$q是从Kris Kowal的Q中获得灵感从而产生的一个promise/deffered实现。

CommonJS Promise方案描述了promise与代表异步操作结果的对象进行交互的操作规范,其中相应的异步操作可能在某一时刻结束,也有可能不结束。

从错误处理方面来看,deffered和promise APIs是针对异步编程模型,而try, catch和throw关键词则是针对同步编程模型。

下面这个例子中,我们假定变量$q和scope在当前上下文中可用,它们或者通过注入方式创建,或者直接传入。

explore metadata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function asyncGreet(name) {
var deferred = $q.defer();

setTimeout(function() {
//因为这个匿名函数会在将来的事件中执行异步操作,我们需要将我们的代码包装在$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);
});

首先,这段代码中展现的处理异步方式是有些复杂的,而且这种复杂性是否值得也不是那么的明显,但是promise和deffered API能够保证异步操作的执行并正确处理返回结果,这是它所带来的好处。

另外, promise接口允许在传统的callback方式下比较难处理的组合场景,比如多个并行或穿行的异步调用,更多的请参考Q文档中关于串行和并行合并promise的章节部分。

1.1 The Deferred API

$q.defer()方法可以创建一个deffered实例。

defferred对象内部包含一个关联的Promise实例,还有用来通知异步任务成功完成与否、任务状态的API接口。

1.1.1 方法

  • resolve(value) – 用给定的值结算派生的promise。如果给定的值是用$q.reject方法构建的,那么promise的状态也使被rejcted。

  • reject(reason) – 拒绝派生的promise。这和用resolve($q.reject))是等价的。

  • notify(value) - 更新promise执行的状态,这个方法可以在promise完成(resovled或者rejected)之前调用多次。

1.1.2 属性

  • promise – {Promise} – 与这个deffered对象关联的promise对象。

1.2 The Promise API

deferred实例创建的同时会创建一个新的promise实例,这个promise实例可以通过deferred.promise获得。

promise对象解决了在defferred任务完成后如何访问结果的问题。

1.2.1 方法

then(successCallback, errorCallback, notifyCallback) – 无论promise什么时候被结算或者拒绝,只要异步任务执行完成返回结果,then方法就会调用成功回调或者异常回调方法。调用回调方法的唯一参数,或者是异步任务执行的结果,或者是执行的原因。另外,在promise结算或者拒绝之前,notify回调方法可以被调用0到多次来获取异步任务执行的进度。

这个方法返回了一个新的promise对象,这个promise对象封装了它接受的成功回调方法,异常回调方法和notify回调方法作为resolve, reject, notify的参数,其它行为和promise对象一样。

catch(errorCallback) – 是 promise.then(null, errorCallback)方法的简写。

finally(callback) – 允许检测promise当前的状态,完成或拒绝。这个方法可以执行一些需要在promise完成后释放资源或者清理的工作。在finally方法中不会修改最终异步任务的结果,具体细节请参见规范。

在JavaScript语言中finally是关键词,ES3不支持将这种关键词作为属性名称。所以如果你的代码需要兼容IE8,那么可以用这种方法调用,promise[‘finally’](callback)。

1.3 Chaining promises

因为then方法又会返回一个新派生的promise,所以用then很容易创建一个链式的promise,如下例。

explore metadata
1
2
3
4
5
promiseB = promiseA.then(function(result) {
return result + 1;
});


promiseB会在promiseA被结算后立即结算,它的结果会是promiseA结果加1。

这个链式promise的长度没有限制,并且因为promise可以被另一个promise结算(会进一步延长它的结算),所以在这个链的任何一点都可以根据异步任务的结果进行暂停或者延长处理,这种机制使得实现诸如$http response interceptors这样的强大API成为可能。

1.4 Differences between Kris Kowal’s Q and $q

主要有三点不同。

  • $q与angular的Scope检测模型集成

这种机制可以更快的将结果传递給你的模型,从而避免了不必要的浏览器重绘,也就避免了界面闪烁的问题。

  • $q promises可以被angular的模版引擎识别
    那么在页面模版中,可以将promise当作结果关联到scope。

  • Q 提供了更多的关于异步操作的功能,不过如果考虑字节大小的话,$q更简洁,它包含了异步任务需要的所有重要功能。

1.5 Testing

explore metadata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();

// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).toBeUndefined();

// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).toEqual(123);
});

2. $q Dependencies

$rootScope

3. $q Methods

3.1 all(promises)

将所有传入的promise合并成一个promise,这个promise将在这些promise全部结算完成后结算。

参数

参数 类型 详细
promises Array.< Promise > Object.< Promise > An array or hash of promises.

返回

返回值
Promise 返回单个promise,可以被一个数组或者哈希值来结算,数组中的每个值就按顺序对应传入的promise的顺序。如果其中一个promise被拒绝了,那么这个返回的promise也会因为同样的原因被拒绝。

3.2 defer()

$q.defer()方法创建了一个Defferred对象,这个deffered对象代表一个在将来时间执行的异步任务。

返回
一个deferred实例 对象

3.3 reject(reason)

创建一个promise,这个api在链式的异步调用中用来转发rejection。最后一个promise不需要考虑这个问题。
与同步的try/catch/throw行为相比,可以认为在JavaScript中reject就是throw,这意味着如果你需要将通过promise的异常回调捕获的异常转发,那么你需要返回一个用reject方法构建的rejection.

explore metadata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
promiseB = promiseA.then(function(result) {
// success: do something and resolve promiseB
// with the old or a new result
return result;
}, function(reason) {
// error: handle the error if possible and
// resolve promiseB with newPromiseOrValue,
// otherwise forward the rejection to promiseB
if (canHandle(reason)) {
// handle the error and recover
return newPromiseOrValue;
}
return $q.reject(reason);
});

参数

参数 类型 详细
reason * 常量、消息、异常或者代表拒绝原因的对象

返回
返回一个已经完成或被拒绝的promise。

3.4 when(value)

将一个对象封装成一个$q的promise对象,这个对象可以是一个值也可以是一个第三方的thenable promise。这个方法适合处理一些特殊对象,这些对象可能不是一个promise,也可能来自一个不确认的第三方。

参数

参数 类型 详细
value * 值或promise

返回
返回一个带有value的promise或者一个空的promise。

Share Comments