创建 Future/Promise 对象

hprose 中提供了多种方法来创建 Future/Promise 对象。为了方便讲解,在后面我们不再详细区分 Future 对象和 Promise 对象实例的差别,统一称为 promise 对象。

使用 Future 构造器

创建一个待定(pending)状态 promise 对象

  1. var Future = hprose.Future;
  2. var promise = new Future();

promise 对象的结果尚未确定,可以在将来通过 resolve 方法来设定其成功值,或通过 reject 方法来设定其失败原因。

创建一个成功(fulfilled)状态的 promise 对象

  1. var Future = hprose.Future;
  2. var promise = new Future(function() { return 'hprose'; });
  3. promise.then(function(value) {
  4. console.log(value);
  5. });

promise 对象中已经包含了成功值,可以使用 then 方法来得到它。

创建一个失败(rejected)状态的 promise 对象

  1. var Future = hprose.Future;
  2. var promise = new Future(function() { throw 'hprose'; });
  3. promise.catch(function(reason) {
  4. console.log(reason);
  5. });

promise 对象中已经包含了失败值,可以使用 catch 方法来得到它。

使用 Future 上的工厂方法

Future 上提供了 6 个工厂方法,它们分别是:

  • value
  • resolve
  • error
  • reject
  • sync
  • delayed
    其中 valueresolve 功能完全相同,errorreject 功能完全相同。valueerror 这两个方法名来自 Dart 语言的 Future 类。而 resolvereject 这两个方法名则来自 ECMAScript 6 的 Promise 对象。因为最初是按照 Dart 语言的 API 设计的,因此,这里保留了 valueerror 这两个方法名。

创建一个成功(fulfilled)状态的 promise 对象

  1. var Future = hprose.Future;
  2. var promise = Future.value('hprose'); // 换成 Future.resolve('hprose') 效果一样
  3. promise.then(function(value) {
  4. console.log(value);
  5. });

使用 valueresolve 来创建一个成功(fulfilled)状态的 promise 对象效果跟前面用 Future 构造器创建的效果一样,但是写起来更加简单,不再需要把结果放入一个函数中作为返回值返回了。

创建一个失败(rejected)状态的 promise 对象

  1. var Future = hprose.Future;
  2. var promise = Future.error('hprose'); // 换成 Future.reject('hprose') 效果一样
  3. promise.catch(function(reason) {
  4. console.log(reason);
  5. });

使用 errorreject 来创建一个失败(rejected)状态的 promise 对象效果跟前面用 Future 构造器创建的效果也一样,但是写起来也更加简单,不再需要把失败原因放入一个函数中作为异常抛出了。

同步创建一个 promise 对象

Future 上提供了一个:

  1. Future.sync(computation)

方法可以让我们同步的创建一个 promise 对象。

这里“同步”的意思是指 computation 的执行是同步执行的。而通过 Future 构造器创建 promise 对象时,computation 是异步执行的。为了可以更好地理解这一点,我们来看一个具体的例子:

  1. var Future = hprose.Future;
  2.  
  3. function async() {
  4. console.log('before Future constructor');
  5. var promise = new Future(function() {
  6. console.log('running Future constructor');
  7. return 'promise from Future constructor';
  8. });
  9. promise.then(function(value) {
  10. console.log(value);
  11. });
  12. console.log('after Future constructor');
  13. }
  14.  
  15. function sync() {
  16. console.log('before Future.sync');
  17. var promise = Future.sync(function() {
  18. console.log('running Future.sync');
  19. return 'promise from Future.sync';
  20. });
  21. promise.then(function(value) {
  22. console.log(value);
  23. });
  24. console.log('after Future.sync');
  25. }
  26.  
  27. async();
  28. sync();

这个程序的执行结果是:


  1. before Future constructor
  2. after Future constructor
  3. before Future.sync
  4. running Future.sync
  5. after Future.sync
  6. running Future constructor
  7. promise from Future.sync
  8. promise from Future constructor

从这里我们可以看出,Future.sync 方法中的 computation 确实是同步执行的,而 Future 构造器中的 computation 也确实是异步执行的。但是对于 then 中回调的执行,却都是异步的。

仔细观察结果,你也许会发现另一个有趣的区别,这里就不再细说了,留给读者自己研究。

另外,前面所说的 Future.valueFuture.resolveFuture.errorFuture.reject 这四个静态方法在创建 promise 对象时,跟使用 Future 构造器也存在同样的差别,但通常你可能不会注意到。

创建一个延迟 promise 对象

虽然通过 Future 构造器来创建一个 promise 对象跟使用 Future.sync 方法来比是异步的,但只是在执行顺序上能看出差别来,但是它并不会让你感到有明显的延时。如果你需要创建一个 promise 对象并且延迟一段时间后再执行 computation 函数,那么你可以使用

  1. Future.delayed(duration, value)

这个方法。

delayed 方法的第一个参数 duration 是一个毫秒值,第二个参数 value 既可以是一个 computation 函数,也可以是一个其它类型的值(包括 promise 对象)。当 value 不是函数时,相当于传入了一个:

  1. function() { return value; };

这样的 computation 函数。

这个 computation 函数会在延迟 duration 毫秒后执行,并将结果或失败原因充填入 promise 对象。

我们来看下面这个例子:

  1. var Future = hprose.Future;
  2.  
  3. function normal() {
  4. console.log(Date.now() + ': before Future constructor');
  5. var promise = new Future(function() {
  6. console.log(Date.now() + ': running Future constructor');
  7. return "promise from Future constructor";
  8. });
  9. promise.then(function(value) {
  10. console.log(Date.now() + ': ' + value);
  11. });
  12. console.log(Date.now() + ': after Future constructor');
  13. }
  14.  
  15. function delayed() {
  16. console.log(Date.now() + ': before Future.delayed');
  17. var promise = Future.delayed(300, function() {
  18. console.log(Date.now() + ': running Future.delayed');
  19. return "promise from Future.delayed";
  20. });
  21. promise.then(function(value) {
  22. console.log(Date.now() + ': ' + value);
  23. });
  24. console.log(Date.now() + ': after Future.delayed');
  25. }
  26.  
  27. normal();
  28. delayed();

该程序的执行结果是:


  1. 1437889453869: before Future constructor
  2. 1437889453871: after Future constructor
  3. 1437889453872: before Future.delayed
  4. 1437889453872: after Future.delayed
  5. 1437889453873: running Future constructor
  6. 1437889453873: promise from Future constructor
  7. 1437889454173: running Future.delayed
  8. 1437889454173: promise from Future.delayed

这个结果一目了然,就不需要多做解释了。

通过 Completer 来创建 promise 对象

  1. var Completer = hprose.Completer;
  2.  
  3. var completer = new Completer();
  4. var promise = completer.future;
  5. promise.then(function(value) {
  6. console.log(value);
  7. });
  8. console.log('isComplete: ' + completer.isCompleted);
  9. completer.complete('hprose')
  10. console.log('isComplete: ' + completer.isCompleted);

运行结果:


  1. isComplete: false
  2. isComplete: true
  3. hprose

Future/Completer 这套 API 来自 Dart 语言,首先通过 Completer 构造器创建一个 completer 对象,然后这个 completer 对象上的 future 属性就是一个 promise 对象。通过 completercomplete 方法可以设置成功值。通过 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.valuehprose.Future.resolve 功能相同。hprose.rejected 方法跟 hprose.Future.errorhprose.Future.reject 功能相同。

hprose.deferred()new Completer() 的作用类似。其中 promise 属性跟 completerfuture 属性作用相同。resolve 方法跟 completercomplete 方法作用相同。reject 方法跟 completercompleteError 方法作用相同。

这里也不再重复举例。

Future.prototype 上的基本方法

then 方法

then 方法是 Promise 的核心和精髓所在。它有两个参数:onFulfilled, onRejected。这两个参数皆为 function 类型。当它们不是 function 类型时,它们将会被忽略。当 promise 对象状态为待定(pending)时,这两个回调方法都不会执行,直到 promise 对象的状态变为成功(fulfilled)或失败(rejected)。当 promise 对象状态为成功(fulfilled)时,onFulfilled 函数会被回调,参数值为成功值。当 promise 对象状态为失败(rejected)时,onRejected 函数会被回调,参数值为失败原因。

then 方法的返回值是一个新的 promise 对象,它的值由 onFulfilledonRejected 的返回值或抛出的异常来决定。如果onFulfilledonRejected 在执行过程中没有抛出异常,那么新的 promise 对象的状态为成功(fulfilled),其值为 onFulfilledonRejected 的返回值。如果这两个回调中抛出了异常,那么新的 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 的增强版,它具有两个参数,第一个参数 onRejectedcatch 方法相同,第二个参数是一个测试函数。当该测试函数省略时,它的效果跟 catch 方法相同。例如:

  1. var Future = hprose.Future;
  2.  
  3. var p = Future.reject(new TypeError('typeError'));
  4.  
  5. p
  6. .catchError(function(reason) { return 'this is a syntax error'; },
  7. function(reason) { return reason instanceof SyntaxError; })
  8. .catchError(function(reason) { return 'this is a type error'; },
  9. function(reason) { return reason instanceof TypeError; })
  10. .then(function(value) { console.log(value); });

输出结果为:


  1. this is a type error

resolve 方法

该方法可以将状态为待定(pending)的 promise 对象变为成功(fulfilled)状态。

该方法的参数值可以为任意类型。

该方法已绑定到它所在的 promise 对象,因此可以安全的作为回调函数进行传递。

reject 方法

该方法可以将状态为待定(pending)的 promise 对象变为失败(rejected)状态。

该方法的参数值可以为任意类型。

该方法已绑定到它所在的 promise 对象,因此可以安全的作为回调函数进行传递。

inspect 方法

该方法返回当前 promise 对象的状态。

如果当前状态为待定(pending),返回值为:

  1. { state: 'pending' }

如果当前状态为成功(fulfilled),返回值为:

  1. { state: 'fulfilled', value: value };

如果当前状态为失败(rejected),返回值为:

  1. { state: 'rejected', reason: reason };

whenComplete 方法

有时候,你不但想要在成功(fulfilled)时执行某段代码,而且在失败(rejected)时也想执行这段代码,那你可以使用 whenComplete 方法。该方法的参数为一个无参回调函数。该方法执行后会返回一个新的 promise 对象,除非在回调函数中抛出异常,否则返回的 promise 对象的值跟原 promise 对象的值相同。

  1. var Future = hprose.Future;
  2.  
  3. var p1 = Future.resolve('resolve hprose');
  4.  
  5. p1.whenComplete(function() { console.log('p1 complete'); })
  6. .then(function(value) { console.log(value); });
  7.  
  8. var p2 = Future.reject('reject thrift');
  9.  
  10. p2.whenComplete(function() { console.log('p2 complete'); })
  11. .catch(function(reason) { console.log(reason); });
  12.  
  13. var p3 = Future.resolve('resolve protobuf');
  14.  
  15. p3.whenComplete(function() { console.log('p3 complete');
  16. throw 'reject protobuf'; })
  17. .catch(function(reason) { console.log(reason); });

运行结果如下:


  1. p1 complete
  2. p2 complete
  3. p3 complete
  4. resolve hprose
  5. reject thrift
  6. reject protobuf

complete 方法

该方法的回调函数 oncomplete 在不论成功还是失败的情况下都会执行,并且支持链式调用。相当于:then(oncomplete, oncomplete) 的简化写法。

该方法的最新版本支持不带参数调用,当不带参数调用时,返回一个新的 promise 对象,该对象会将源 promise 对象的失败(rejected)值转换为成功(fulfilled)值,这样在后面可以直接使用 then 的第一个回调参数统一处理。它的主要作用是当配合协程一起使用时,可以避免使用 try catch 来捕获异常。

always 方法

该方法的回调函数 oncomplete 在不论成功还是失败的情况下都会执行,但不支持链式调用。相当于:done(oncomplete, oncomplete) 的简化写法。

如果您不是在写单元测试,最好不要使用 always 方法。

fill 方法

将当前 promise 对象的值充填到参数所表示的 promise 对象中。