创建 Future/Promise 对象
hprose 中提供了多种方法来创建 Future/Promise 对象。为了方便讲解,在后面我们不再详细区分 Future 对象和 Promise 对象实例的差别,统一称为 promise
对象。
使用 Future 构造器
创建一个待定(pending)状态 promise 对象
- var Future = hprose.Future;
- var promise = new Future();
该 promise
对象的结果尚未确定,可以在将来通过 resolve
方法来设定其成功值,或通过 reject
方法来设定其失败原因。
创建一个成功(fulfilled)状态的 promise 对象
- var Future = hprose.Future;
- var promise = new Future(function() { return 'hprose'; });
- promise.then(function(value) {
- console.log(value);
- });
该 promise
对象中已经包含了成功值,可以使用 then
方法来得到它。
创建一个失败(rejected)状态的 promise 对象
- var Future = hprose.Future;
- var promise = new Future(function() { throw 'hprose'; });
- promise.catch(function(reason) {
- console.log(reason);
- });
该 promise
对象中已经包含了失败值,可以使用 catch
方法来得到它。
使用 Future 上的工厂方法
Future
上提供了 6 个工厂方法,它们分别是:
- value
- resolve
- error
- reject
- sync
- delayed
其中value
和resolve
功能完全相同,error
和reject
功能完全相同。value
和error
这两个方法名来自 Dart 语言的Future
类。而resolve
和reject
这两个方法名则来自 ECMAScript 6 的 Promise 对象。因为最初是按照 Dart 语言的 API 设计的,因此,这里保留了value
和error
这两个方法名。
创建一个成功(fulfilled)状态的 promise 对象
- var Future = hprose.Future;
- var promise = Future.value('hprose'); // 换成 Future.resolve('hprose') 效果一样
- promise.then(function(value) {
- console.log(value);
- });
使用 value
或 resolve
来创建一个成功(fulfilled)状态的 promise
对象效果跟前面用 Future
构造器创建的效果一样,但是写起来更加简单,不再需要把结果放入一个函数中作为返回值返回了。
创建一个失败(rejected)状态的 promise 对象
- var Future = hprose.Future;
- var promise = Future.error('hprose'); // 换成 Future.reject('hprose') 效果一样
- promise.catch(function(reason) {
- console.log(reason);
- });
使用 error
或 reject
来创建一个失败(rejected)状态的 promise
对象效果跟前面用 Future
构造器创建的效果也一样,但是写起来也更加简单,不再需要把失败原因放入一个函数中作为异常抛出了。
同步创建一个 promise 对象
Future
上提供了一个:
- Future.sync(computation)
方法可以让我们同步的创建一个 promise
对象。
这里“同步”的意思是指 computation
的执行是同步执行的。而通过 Future
构造器创建 promise
对象时,computation
是异步执行的。为了可以更好地理解这一点,我们来看一个具体的例子:
- var Future = hprose.Future;
- function async() {
- console.log('before Future constructor');
- var promise = new Future(function() {
- console.log('running Future constructor');
- return 'promise from Future constructor';
- });
- promise.then(function(value) {
- console.log(value);
- });
- console.log('after Future constructor');
- }
- function sync() {
- console.log('before Future.sync');
- var promise = Future.sync(function() {
- console.log('running Future.sync');
- return 'promise from Future.sync';
- });
- promise.then(function(value) {
- console.log(value);
- });
- console.log('after Future.sync');
- }
- async();
- sync();
这个程序的执行结果是:
before Future constructor
after Future constructor
before Future.sync
running Future.sync
after Future.sync
running Future constructor
promise from Future.sync
promise from Future constructor
从这里我们可以看出,Future.sync
方法中的 computation
确实是同步执行的,而 Future
构造器中的 computation
也确实是异步执行的。但是对于 then
中回调的执行,却都是异步的。
仔细观察结果,你也许会发现另一个有趣的区别,这里就不再细说了,留给读者自己研究。
另外,前面所说的 Future.value
、Future.resolve
、Future.error
、Future.reject
这四个静态方法在创建 promise
对象时,跟使用 Future
构造器也存在同样的差别,但通常你可能不会注意到。
创建一个延迟 promise 对象
虽然通过 Future
构造器来创建一个 promise
对象跟使用 Future.sync
方法来比是异步的,但只是在执行顺序上能看出差别来,但是它并不会让你感到有明显的延时。如果你需要创建一个 promise
对象并且延迟一段时间后再执行 computation
函数,那么你可以使用
- Future.delayed(duration, value)
这个方法。
delayed
方法的第一个参数 duration
是一个毫秒值,第二个参数 value
既可以是一个 computation
函数,也可以是一个其它类型的值(包括 promise
对象)。当 value
不是函数时,相当于传入了一个:
- function() { return value; };
这样的 computation
函数。
这个 computation
函数会在延迟 duration
毫秒后执行,并将结果或失败原因充填入 promise
对象。
我们来看下面这个例子:
- var Future = hprose.Future;
- function normal() {
- console.log(Date.now() + ': before Future constructor');
- var promise = new Future(function() {
- console.log(Date.now() + ': running Future constructor');
- return "promise from Future constructor";
- });
- promise.then(function(value) {
- console.log(Date.now() + ': ' + value);
- });
- console.log(Date.now() + ': after Future constructor');
- }
- function delayed() {
- console.log(Date.now() + ': before Future.delayed');
- var promise = Future.delayed(300, function() {
- console.log(Date.now() + ': running Future.delayed');
- return "promise from Future.delayed";
- });
- promise.then(function(value) {
- console.log(Date.now() + ': ' + value);
- });
- console.log(Date.now() + ': after Future.delayed');
- }
- normal();
- delayed();
该程序的执行结果是:
1437889453869: before Future constructor
1437889453871: after Future constructor
1437889453872: before Future.delayed
1437889453872: after Future.delayed
1437889453873: running Future constructor
1437889453873: promise from Future constructor
1437889454173: running Future.delayed
1437889454173: promise from Future.delayed
这个结果一目了然,就不需要多做解释了。
通过 Completer 来创建 promise 对象
- var Completer = hprose.Completer;
- var completer = new Completer();
- var promise = completer.future;
- promise.then(function(value) {
- console.log(value);
- });
- console.log('isComplete: ' + completer.isCompleted);
- completer.complete('hprose')
- console.log('isComplete: ' + completer.isCompleted);
运行结果:
isComplete: false
isComplete: true
hprose
Future/Completer
这套 API 来自 Dart 语言,首先通过 Completer
构造器创建一个 completer
对象,然后这个 completer
对象上的 future
属性就是一个 promise
对象。通过 completer
的 complete
方法可以设置成功值。通过 completeError
方法可以设置失败原因。通过 isCompleted
属性,可以查看当前状态是否为已完成(在这里,成功(fulfilled)或失败(rejected)都算完成状态)。
在 hprose 2.0 之前的版本中,这是唯一可用的方法。但在 hprose 2.0 中,该方式已经被其他方式所代替。仅为兼容旧版本而保留。
通过 ECMAScript 6 方式来创建 promise 对象
hprose 提供了 ECMAScript 6 的 Promise
对象的兼容实现。
具体使用方式可以直接参见该文档:MDN: Promise
这里就不再重复了。
hprose-html5.js 里面已经自动创建了全局的 Promise
实现,如果检测已有内置实现或者其它第三方实现的话,将不会替换成 hprose 版本的。
使用该方式创建的 promise
对象,你应该只使用 ECMAScript 6 文档中记载的 API,而不能使用本文档中的大部分 API。因此,在使用 hprose 时,并不推荐使用此方式创建 promise
对象。
通过 Future.promise 方法来创建 promise 对象
该方法的参数跟 ECMAScript 6 的 Promise
构造器的参数相同,不同的是,使用该方法创建 promise
对象时,不需要使用 new
关键字。另外一点不同是,该方法创建的 promise
对象一定是 Future
的实例对象,而通过 Promise
构造器创建的 promise
对象可能会是内置或第三方的 Promise
实例对象。
因此,推荐使用该方法来代替 ECMAScript 6 的 Promise
构造器方式。
通过 hprose 上的方法来创建 promise 对象
Promises/A+ Compliance Test Suite 上提供了一套用于测试是否符合 Promises/A+ 规范的最小适配器接口。hprose 对象上已经实现了这套接口,即:
- hprose.resolved(value)
- hprose.rejected(reason)
- hprose.deferred()
- promise
- resolve(value)
- reject(reason)
当然,这套接口不仅仅可以用来测试。你也可以用于实际用途。
其中,hprose.resolved
方法跟 hprose.Future.value
、hprose.Future.resolve
功能相同。hprose.rejected
方法跟 hprose.Future.error
、hprose.Future.reject
功能相同。
而 hprose.deferred()
跟 new Completer()
的作用类似。其中 promise
属性跟 completer
的 future
属性作用相同。resolve
方法跟 completer
的 complete
方法作用相同。reject
方法跟 completer
的 completeError
方法作用相同。
这里也不再重复举例。
Future.prototype 上的基本方法
then 方法
then
方法是 Promise
的核心和精髓所在。它有两个参数:onFulfilled
, onRejected
。这两个参数皆为 function
类型。当它们不是 function
类型时,它们将会被忽略。当 promise
对象状态为待定(pending)时,这两个回调方法都不会执行,直到 promise
对象的状态变为成功(fulfilled)或失败(rejected)。当 promise
对象状态为成功(fulfilled)时,onFulfilled
函数会被回调,参数值为成功值。当 promise
对象状态为失败(rejected)时,onRejected
函数会被回调,参数值为失败原因。
then
方法的返回值是一个新的 promise
对象,它的值由 onFulfilled
或 onRejected
的返回值或抛出的异常来决定。如果onFulfilled
或 onRejected
在执行过程中没有抛出异常,那么新的 promise
对象的状态为成功(fulfilled),其值为 onFulfilled
或 onRejected
的返回值。如果这两个回调中抛出了异常,那么新的 promise
对象的状态将被设置为失败(rejected),抛出的异常作为新的 promise
对象的失败原因。
then
方法的 onFulfilled
, onRejected
这两个回调函数是异步执行的,即使当前的 promise
对象的状态为已完成(fulfilled 或 rejected)。
同一个 promise
对象的 then
方法可以被多次调用,其值不会因为调用 then
方法而改变。当 then
方法被多次调用时,所有的 onFulfilled
, onRejected
将按照原始的调用顺序被执行。
因为 then
方法的返回值还是一个 promise
对象,因此可以使用链式调用的方式实现异步编程串行化。
当 promise
的成功值被设置为另一个 promise
对象(为了区分,将其命名为 promise2
)时,then
方法中的两个回调函数得到的参数是 promise2
对象的最终展开值,而不是 promise2
对象本身。当 promise2
的最终展开值为成功值时,onFulfilled
函数会被调用,当 promise2
的最终展开值为失败原因时,onRejected
函数会被调用。
当 promise
的失败原因被设置为另一个 promise
对象时,该对象会直接作为失败原因传给 then
方法的 onRejected
回调函数。
then
方法是 Promise/A+ 规范的完整实现。
具体使用方法可参见:MDN: Promise.prototype.then()
done 方法
跟 then
方法类似,但 done
方法没有返回值,不支持链式调用,因此在 done
方法的回调函数中,通常不会返回值。
如果在 done
方法的回调中发生异常,会直接抛出,并且无法被捕获。
因此,如果您不是在写单元测试,最好不要使用 done
方法。
catch 方法
该方法是 then(null, onRejected)
的简化写法。
具体使用方法可参见:MDN: Promise.prototype.catch()
fail 方法
该方法是 done(null, onRejected)
的简化方法。
如果您不是在写单元测试,最好不要使用 fail
方法。
catchError 方法
该方法是 catch
的增强版,它具有两个参数,第一个参数 onRejected
跟 catch
方法相同,第二个参数是一个测试函数。当该测试函数省略时,它的效果跟 catch
方法相同。例如:
- var Future = hprose.Future;
- var p = Future.reject(new TypeError('typeError'));
- p
- .catchError(function(reason) { return 'this is a syntax error'; },
- function(reason) { return reason instanceof SyntaxError; })
- .catchError(function(reason) { return 'this is a type error'; },
- function(reason) { return reason instanceof TypeError; })
- .then(function(value) { console.log(value); });
输出结果为:
this is a type error
resolve 方法
该方法可以将状态为待定(pending)的 promise
对象变为成功(fulfilled)状态。
该方法的参数值可以为任意类型。
该方法已绑定到它所在的 promise
对象,因此可以安全的作为回调函数进行传递。
reject 方法
该方法可以将状态为待定(pending)的 promise
对象变为失败(rejected)状态。
该方法的参数值可以为任意类型。
该方法已绑定到它所在的 promise
对象,因此可以安全的作为回调函数进行传递。
inspect 方法
该方法返回当前 promise
对象的状态。
如果当前状态为待定(pending),返回值为:
- { state: 'pending' }
如果当前状态为成功(fulfilled),返回值为:
- { state: 'fulfilled', value: value };
如果当前状态为失败(rejected),返回值为:
- { state: 'rejected', reason: reason };
whenComplete 方法
有时候,你不但想要在成功(fulfilled)时执行某段代码,而且在失败(rejected)时也想执行这段代码,那你可以使用 whenComplete
方法。该方法的参数为一个无参回调函数。该方法执行后会返回一个新的 promise
对象,除非在回调函数中抛出异常,否则返回的 promise
对象的值跟原 promise
对象的值相同。
- var Future = hprose.Future;
- var p1 = Future.resolve('resolve hprose');
- p1.whenComplete(function() { console.log('p1 complete'); })
- .then(function(value) { console.log(value); });
- var p2 = Future.reject('reject thrift');
- p2.whenComplete(function() { console.log('p2 complete'); })
- .catch(function(reason) { console.log(reason); });
- var p3 = Future.resolve('resolve protobuf');
- p3.whenComplete(function() { console.log('p3 complete');
- throw 'reject protobuf'; })
- .catch(function(reason) { console.log(reason); });
运行结果如下:
p1 complete
p2 complete
p3 complete
resolve hprose
reject thrift
reject protobuf
complete 方法
该方法的回调函数 oncomplete
在不论成功还是失败的情况下都会执行,并且支持链式调用。相当于:then(oncomplete, oncomplete)
的简化写法。
该方法的最新版本支持不带参数调用,当不带参数调用时,返回一个新的 promise 对象,该对象会将源 promise 对象的失败(rejected)值转换为成功(fulfilled)值,这样在后面可以直接使用 then
的第一个回调参数统一处理。它的主要作用是当配合协程一起使用时,可以避免使用 try
catch
来捕获异常。
always 方法
该方法的回调函数 oncomplete
在不论成功还是失败的情况下都会执行,但不支持链式调用。相当于:done(oncomplete, oncomplete)
的简化写法。
如果您不是在写单元测试,最好不要使用 always
方法。
fill 方法
将当前 promise
对象的值充填到参数所表示的 promise
对象中。