Skip to content

Promise 异步编程

小马哥 edited this page Nov 19, 2016 · 1 revision
Promises/A+ logo

概述

JavaScript 层层回调的异步编程让人望而生畏。而 Promise 的诞生就是为了解决这个问题,它提供了一种 Future 模式,大大简化了异步编程的复杂性。而 Promise/A+(中文版)是一个通用的、标准化的规范,它提供了一个可互操作的 then 方法的实现定义。Promise/A+ 规范的实现有很多,它们的共同点就是都有一个标准的 then 方法,而其它的 API 则各不相同。

ECMAScript 6 提供了一套 Promise 的标准实现,目前大部分浏览器和较新版本的 Node.js 也都已经支持,但老旧的浏览器和旧版本的 Node.js 上是没有内置 Promise 实现的。另外,ECMAScript 6 这套 Promise 实现,所提供的 API 也是极其有限,只能满足基本需求。

为了能够有一套统一的 Promise 实现,并且不依赖第三方库,hprose 自己实现了一套完全兼容 Promise/A+ 规范的 API。

hprose 2.0 之前的版本提供了一组 Future/Completer 的 API,其中 Future 对象上也提供了 then 方法,但并不完全兼容 Promise/A+ 规范。它最初是参照 Dart 语言中的 Future/Completer 设计的。

而在 hprose 2.0 版本中,我们对 Future 的实现做了比较大的改进,现在它既兼容 Dart 的 Future/Completer 使用方式,又完全兼容 Promise/A+ 规范,而且还增加了许多非常实用的方法。下面我们就来对这些方法做一个全面的介绍。

创建 Future/Promise 对象

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

使用 Future 构造器

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

var hprose = require('hprose');
var Future =  hprose.Future;
var promise = new Future();

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

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

var hprose = require('hprose');
var Future =  hprose.Future;
var promise = new Future(function() { return 'hprose'; });
promise.then(function(value) {
    console.log(value);
});

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

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

var hprose = require('hprose');
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

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

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

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

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

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

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

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

同步创建一个 promise 对象

Future 上提供了一个:

Future.sync(computation)

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

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

var hprose = require('hprose');
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.valueFuture.resolveFuture.errorFuture.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 hprose = require('hprose');
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 hprose = require('hprose');
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 对象。通过 completercomplete 方法可以设置成功值。通过 completeError 方法可以设置失败原因。通过 isCompleted 属性,可以查看当前状态是否为已完成(在这里,成功(fulfilled)或失败(rejected)都算完成状态)。

在 hprose 2.0 之前的版本中,这是唯一可用的方法。但在 hprose 2.0 中,该方式已经被其他方式所代替。仅为兼容旧版本而保留。

通过 ECMAScript 6 方式来创建 promise 对象

hprose 提供了 ECMAScript 6 的 Promise对象的兼容实现。

具体使用方式可以直接参见该文档:MDN: Promise

这里就不再重复了。唯一需要注意的是,如果需要在所有平台上都能使用,需要包含一下 hprose 的包:

require('hprose');

该包里面已经自动创建了全局的 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)

因此你可以直接使用以下方式来进行测试:

var hprose = require("hprose");
var promisesAplusTests = require("promises-aplus-tests");

promisesAplusTests(hprose, function (err) {
    // All done; output is in the console. Or check `err` for number of failures.
});

当然,这套接口不仅仅可以用来测试。你也可以用于实际用途。

其中,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 方法的回调中发生异常,会直接抛出,并且无法被捕获。

catch 方法

该方法是 then(null, onRejected) 的简化写法。

具体使用方法可参见:MDN: Promise.prototype.catch()

fail 方法

该方法是 done(null, onRejected) 的简化方法。

catchError 方法

该方法是 catch 的增强版,它具有两个参数,第一个参数 onRejectedcatch 方法相同,第二个参数是一个测试函数。当该测试函数省略时,它的效果跟 catch 方法相同。例如:

var hprose = require('hprose');
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 hprose = require('hprose');
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) 的简化写法。

fill 方法

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

Future 上的辅助方法

isFuture 方法

Future.isFuture(obj)

用来判断是否是 Future 的实例对象。

isPromise 方法

Future.isPromise(obj)

用来判断是否是 Future 或 ECMAScript 6 的 Promise 实例对象。

注意,该方法对其它符合 Promise/A+ 规范实现的 thenable 对象进行判断的返回值是 false

如果你需要一个 promise 对象,保险的做法是用 Future.toPromise() 方法包装一下。

toPromise 方法

Future.toPromise(obj)

如果 obj 是一个 Promise 对象,那么直接返回 obj,否则返回 Future.value(obj)

all 方法

Future.all(array)

该方法返回一个 promise 对象,该 promise 对象会在数组参数内的所有 promise 都被设置为成功(fulfilled)状态时,才被设置为成功(fulfilled)状态,其值为数组参数中所有 promise 对象的最终展开值组成的数组,其数组元素与原数组元素一一对应。

具体使用方法可参见:MDN: Promise.all()

Future.all 方法与 Promise.all 方法在参数上有一点区别,Future.all 方法的数组参数本身也可以是一个值为数组的 promise 对象。

race 方法

Future.race(array)

该方法返回一个 promise 对象,这个 promise 在数组参数中的任意一个 promise 被设置为成功(fulfilled)或失败(rejected)后,立刻以相同的成功值被设置为成功(fulfilled)或以相同的失败原因被设置为失败(rejected)。

具体使用方法可参见:MDN: Promise.race()

Future.race 方法与 Promise.race 方法在参数上有一点区别,Future.race 方法的数组参数本身也可以是一个值为数组的 promise 对象。

join 方法

Future.join([arg1[, arg2[, arg3...]]]);

该方法的功能同 all 方法类似,但它与 all 方法的参数不同,我们来举例看一下它们的差别:

var hprose = require('hprose');
var Future = hprose.Future;

var promise = Future.resolve(3);

Future.all([true, promise])
      .then(function(values) {
          console.log(values);
       });

Future.join(true, promise)
      .then(function(values) {
          console.log(values);
       });

输出结果如下:

[ true, 3 ]
[ true, 3 ]

any 方法

Future.any(array)

该方法是 race 方法的改进版。

对于 race 方法,如果输入的数组为空,返回的 promise 对象将永远保持为待定(pending)状态。

而对于 any 方法,如果输入的数组为空,返回的 promise 对象将被设置为失败状态,失败原因是一个 RangeError 对象。

对于 race 方法,数组参数中的任意一个 promise 被设置为成功(fulfilled)或失败(rejected)后,返回的 promise 对象就会被设定为成功(fulfilled)或失败(rejected)状态。

而对于 any 方法,只有当数组参数中的所有 promise 被设置为失败状态时,返回的 promise 对象才会被设定为失败状态。否则,返回的 promise 对象被设置为第一个被设置为成功(fulfilled)状态的成功值。

settle 方法

Future.settle(array)

该方法返回一个 promise 对象,该 promise 对象会在数组参数内的所有 promise 都被设置为成功(fulfilled)状态或失败(fulfilled)状态时,才被设置为成功(fulfilled)状态,其值为数组参数中所有 promise 对象的 inspect 方法返回值,其数组元素与原数组元素一一对应。

例如:

var hprose = require('hprose');
var Future = hprose.Future;

var p1 = Future.resolve(3);
var p2 = Future.reject("x");

Future.settle([true, p1, p2])
      .then(function(values) {
          console.log(values);
       });

输出结果为:

[ { state: 'fulfilled', value: true },
  { state: 'fulfilled', value: 3 },
  { state: 'rejected', reason: 'x' } ]

attempt 方法

Future.attempt(handler[, arg1[, arg2[, arg3...]]]);

attempt 方法的作用是异步执行 handler 函数并返回一个包含执行结果的 promise 对象,handler 的参数分别为 arg1, arg2, arg3 ...。参数可以是普通值,也可以是 promise 对象,如果是 promise 对象,则等待其变为成功(fulfilled)状态时再将其成功值代入 handler 函数。如果变为失败(rejected)状态,attempt 返回的 promise 对象被设置为该失败原因。如果参数中,有多个 promise 对象变为失败(rejected)状态,则第一个变为失败状态的 promise 对象的失败原因被设置为 attempt 返回的 promise 对象的失败原因。当参数中的 promise 对象都变为成功(fulfilled)状态时,handler 函数才会执行,如果在 handler 执行的过程中,抛出了异常,则该异常作为 attempt 返回的 promise 对象的失败原因。如果没有异常,则 handler 函数的返回值,作为 attempt 返回的 promise 对象的成功值。

var hprose = require('hprose');
var Future = hprose.Future;

function add(a, b) {
    return a + b;
}

var p1 = Future.resolve(3);

Future.attempt(add, 2, p1)
      .then(function(value) {
          console.log(value);
      });

输出结果为:

5

run 方法

Future.run(handler[, thisArg[, arg1[, arg2[, arg3...]]]]);

run 方法跟上面的 attempt 方法功能类似,唯一的区别是 run 方法的第二个参数为 thisArg,他表示 handler 的执行上下文。当 thisArg 的值为 undefine 时,行为跟 attempt 方法完全一致。

例如:

var hprose = require('hprose');
var Future = hprose.Future;

function add(a, b) {
    return a + b;
}

var p1 = Future.resolve(3);

Future.run(console.log, console, Future.attempt(add, 2, p1));

输出结果为:

5

wrap 方法

Future.wrap(handler[, thisArg]);

wrap 方法返回一个包装好的函数,该函数的执行方式跟使用 Future.run 的效果一样。例如:

var hprose = require('hprose');
var Future = hprose.Future;

var add = Future.wrap(function(a, b) {
    return a + b;
});

var log = Future.wrap(console.log, console);

var p1 = Future.resolve(3);

log(add(2, p1));

输出结果为:

5

forEach 方法

Future.forEach(array, callback[, thisArg])

该方法你可以认为是 Array.forEachpromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。如果在 callback 回调中抛出了异常,则该方法返回的 promise 对象也被设置为失败(rejected)状态,失败原因被设置为抛出的异常值。

var hprose = require('hprose');
var Future = hprose.Future;

function logArrayElements(element, index, array) {
  console.log('a[' + index + '] = ' + element);
}

// Note elision, there is no member at 2 so it isn't visited
Future.forEach([2, Future.value(5), , 9], logArrayElements);

输出结果为:

a[0] = 2
a[1] = 5
a[3] = 9

every 方法

Future.every(array, callback[, thisArg])

该方法你可以认为是 Array.everypromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。如果在 callback 回调中抛出了异常,则该方法返回的 promise 对象也被设置为失败(rejected)状态,失败原因被设置为抛出的异常值。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isBigEnough(element, index, array) {
  return element >= 10;
}

var a1 = [12, Future.value(5), 8, Future.value(130), 44];
var a2 = [12, Future.value(54), 18, Future.value(130), 44];
var a3 = Future.value(a1);
var a4 = Future.value(a2);
log(Future.every(a1, isBigEnough));   // false
log(Future.every(a2, isBigEnough));   // true
log(Future.every(a3, isBigEnough));   // false
log(Future.every(a4, isBigEnough));   // true

输出结果为:

false
true
false
true

some 方法

Future.some(array, callback[, thisArg])

该方法你可以认为是 Array.somepromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。如果在 callback 回调中抛出了异常,则该方法返回的 promise 对象也被设置为失败(rejected)状态,失败原因被设置为抛出的异常值。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isBiggerThan10(element, index, array) {
  return element > 10;
}

var a1 = [2, Future.value(5), 8, Future.value(1), 4];
var a2 = [12, Future.value(5), 8, Future.value(1), 4];
var a3 = Future.value(a1);
var a4 = Future.value(a2);
log(Future.some(a1, isBiggerThan10));   // false
log(Future.some(a2, isBiggerThan10));   // true
log(Future.some(a3, isBiggerThan10));   // false
log(Future.some(a4, isBiggerThan10));   // true

输出结果为:

false
true
false
true

filter 方法

Future.filter(array, callback[, thisArg])

该方法你可以认为是 Array.filterpromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。如果在 callback 回调中抛出了异常,则该方法返回的 promise 对象也被设置为失败(rejected)状态,失败原因被设置为抛出的异常值。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isBigEnough(value) {
  return value >= 10;
}

var a1 = [12, Future.value(5), 8, Future.value(130), 44];
var a2 = Future.value(a1);
log(Future.filter(a1, isBigEnough));
log(Future.filter(a2, isBigEnough));

输出结果为:

[ 12, 130, 44 ]
[ 12, 130, 44 ]

map 方法

Future.map(array, callback[, thisArg])

该方法你可以认为是 Array.mappromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。如果在 callback 回调中抛出了异常,则该方法返回的 promise 对象也被设置为失败(rejected)状态,失败原因被设置为抛出的异常值。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var numbers = [1, Future.value(4), Future.value(9)];
log(Future.map(numbers, Math.sqrt));
log(Future.map(numbers, function(num) {
  return num * 2;
}));

输出结果为:

[ 1, 2, 3 ]
[ 2, 8, 18 ]

reduce 方法

Future.reduce(array, callback[, initialValue])

该方法你可以认为是 Array.reducepromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。如果在 callback 回调中抛出了异常,则该方法返回的 promise 对象也被设置为失败(rejected)状态,失败原因被设置为抛出的异常值。

initialValue 的值也可以是一个 promise 对象。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var numbers = [Future.value(0), 1, Future.value(2), 3, Future.value(4)];

function callback(previousValue, currentValue, index, array) {
  return previousValue + currentValue;
}

log(Future.reduce(numbers, callback));
log(Future.reduce(numbers, callback, 10));
log(Future.reduce(numbers, callback, Future.value(20)));

输出结果为:

10
20
30

reduceRight 方法

Future.reduceRight(array, callback[, initialValue])

该方法你可以认为是 Array.reduceRightpromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。如果在 callback 回调中抛出了异常,则该方法返回的 promise 对象也被设置为失败(rejected)状态,失败原因被设置为抛出的异常值。

initialValue 的值也可以是一个 promise 对象。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function concat(a, b) {
    return a.concat(b);
}

var array = [[0, 1], Future.value([2, 3]), Future.value([4, 5])];

log(Future.reduceRight(array, concat, []));
log(Future.reduceRight(array, concat, Future.value([6, 7])));

输出结果为:

[ 4, 5, 2, 3, 0, 1 ]
[ 6, 7, 4, 5, 2, 3, 0, 1 ]

indexOf 方法

Future.indexOf(array, searchElement[, fromIndex])

该方法你可以认为是 Array.indexOfpromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。

searchElement 的值也可以是一个 promise 对象。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var array = [1, Future.value(2), Future.value(3)];

log(Future.indexOf(array, 2));
log(Future.indexOf(array, Future.value(3), 1));
log(Future.indexOf(array, 1, 1));

输出结果为:

1
2
-1

lastIndexOf 方法

Future.lastIndexOf(array, searchElement[, fromIndex])

该方法你可以认为是 Array.lastIndexOfpromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。

searchElement 的值也可以是一个 promise 对象。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var array = [1, Future.value(2), Future.value(3)];

log(Future.lastIndexOf(array, 2));
log(Future.lastIndexOf(array, Future.value(3), 1));
log(Future.lastIndexOf(array, 1, 1));

输出结果为:

1
-1
0

includes 方法

Future.includes(array, searchElement[, fromIndex])

该方法你可以认为是 Array.includespromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。

searchElement 的值也可以是一个 promise 对象。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var array = [1, Future.value(2), Future.value(3)];

log(Future.includes(array, 2));
log(Future.includes(array, Future.value(3), 1));
log(Future.includes(array, 1, 1));

输出结果为:

true
true
false

find 方法

Future.find(array, predicate[, thisArg])

该方法你可以认为是 Array.findpromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。predicate 是一个回调方法。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

var array1 = [4, Future.value(6), 8, Future.value(12)];
var array2 = [4, Future.value(5), 7, Future.value(12)];

log(Future.find(array1, isPrime));
log(Future.find(array2, isPrime));

输出结果为:

undefined
5

findIndex 方法

Future.findIndex(array, predicate[, thisArg])

该方法你可以认为是 Array.findIndexpromise 版本,其中参数 array 可以是一个包含了 promise 元素的数组,也可以是一个包含了数组的 promise 对象。返回值是一个 promise 对象。如果参数数组中的 promise 对象为失败(rejected)状态,则该方法返回的 promise 对象被设置为失败(rejected)状态,且设为相同失败原因。predicate 是一个回调方法。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

var array1 = [4, Future.value(6), 8, Future.value(12)];
var array2 = [4, Future.value(5), 7, Future.value(12)];

log(Future.findIndex(array1, isPrime));
log(Future.findIndex(array2, isPrime));

输出结果为:

-1
1

Future.prototype 上的辅助方法

timeout 方法

Future.prototype.timeout(duration[, reason])

创建一个新的 promise 对象,当超过设定的时间 duration(单位毫秒),源 promise 对象如果还未被设置为成功(fulfilled)或失败(rejected),则新的 promise 对象被设置为一个 TimeoutError 或者自定义的 reason。否则,其值跟源 promise 相同。

var hprose = require('hprose');
var Future = hprose.Future;

var add = Future.wrap(function(a, b) {
    return a + b;
});

var log = Future.wrap(console.log, console);

var p1 = Future.delayed(200, 3).timeout(300);
var p2 = Future.delayed(500, 3).timeout(300);

log(add(p1, 2)).catch(log);
log(add(p2, 5)).catch(log);

输出结果为:

5
{ [TimeoutError: timeout] message: 'timeout', name: 'TimeoutError' }

delay 方法

Future.prototype.delay(duration)

创建一个新的 promise 对象,当经过 duration 毫秒后,该 promise 对象的值将被设置为跟源 promise 对象相同的成功值。如果源 promise 对象被设置为失败(rejected)状态,新的 promise 对象将立即被设为相同的失败原因,而无等待。

var hprose = require('hprose');
var Future = hprose.Future;

var add = Future.wrap(function(a, b) {
    return a + b;
});

var log = Future.wrap(console.log, console);

var p = Future.value(3);
var p1 = p.delay(200).timeout(300);
var p2 = p.delay(500).timeout(300);

log(add(p1, 2)).catch(log);
log(add(p2, 5)).catch(log);

输出结果为:

5
{ [TimeoutError: timeout] message: 'timeout', name: 'TimeoutError' }

tap 方法

Future.prototype.tap(onfulfilledSideEffect[, thisArg])

以下两种写法是等价的:

promise.then(function(result) {
    onfulfilledSideEffect.call(thisArg, result);
    return result;
});

promise.tap(onfulfilledSideEffect, thisArg);

spread 方法

Future.prototype.spread(onfulfilledArray[, thisArg])

以下两种写法是等价的:

promise.then(function(array) {
    return onfulfilledArray.apply(thisArg, array);
});

promise.spread(onfulfilledArray, thisArg);

get 方法

Future.prototype.get(key)

以下两种写法是等价的:

promise.then(function(result) {
    return result[key];
});

promise.get(key);

set 方法

Future.prototype.set(key, value)

以下两种写法是等价的:

promise.then(function(result) {
    result[key] = value;
    return result;
});

promise.set(key, value);

下面我们来看一个例子:

var hprose = require('hprose');
var Future = hprose.Future;

function User() {
    this.name = "Tom";
    this.age = 18;
}

var log = Future.wrap(console.log, console);

var p = Future.value(new User());

p.get('name').then(function(result) { console.log(result); });
log(p.get('age'));
p.set('password', 'hprose')
 .tap(log)
 .tap(function(result) { console.log(result); })
 .set('password', 'I love hprose!')
 .get('password')
 .then(log);

输出结果:

Tom
{ name: 'Tom', age: 18, password: 'hprose' }
18
{ name: 'Tom', age: 18, password: 'I love hprose!' }
I love hprose!

注意上面的结果中,被 Future.wrap 包装过的 log 函数在执行时,并不是按照代码书写顺序执行的,也不是按照链式调用顺序执行的,它比在代码中书写的位置执行的要晚,因为被包装过的函数本身也是异步执行的,因此在输出结果上会比较反直觉。

比如上面输出结果中的这一句:

{ name: 'Tom', age: 18, password: 'I love hprose!' }

实际上是这一句代码:

 .tap(log)

输出的。

因此,在链式调用中使用 Future.wrap 包装过的函数,一定要注意这点。后面介绍的 bind 方法也同样需要注意这个问题。

apply 方法

Future.prototype.apply(method[, args])

异步执行当前 promise 对象上的方法名为 method 的方法,参数为 args 数组中的元素,返回结果为包含了执行结果的 promise 对象。其中参数数组中的元素也可以为 promise 对象,在执行时,会带入这些 promise 对象所包含的成功值,如果这些 promise 对象中包含有失败状态的,则 method 方法不会执行,返回的 promise 对象中包含的失败原因为第一个变为失败(rejected)状态的参数的失败原因。

例如:

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var p = Future.value([]);

p.apply('push', ['banana', Future.value('apple'), 'peach'])
 .then(function(length) {
    console.log(length);
    log(p);
 });

p.apply('push', ['banana', Future.error('apple'), 'peach'])
 .catch(function(reason) {
    console.log(reason);
 });

输出结果为:

apple
3
[ 'banana', 'apple', 'peach' ]

因为第二个 apply 的参数中包含有失败(rejected)状态的 promise 参数,因此第二个 apply 中的 push 方法并没有执行而提前返回,所以失败原因 apple 被先显示出来。

call 方法

Future.prototype.call(method[, arg1[, arg2[, arg3...]]])

与上面的 apply 方法类似,唯一不同的是参数。看下面的例子:

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var p = Future.value([]);

p.call('push', 'banana', Future.value('apple'), 'peach')
 .then(function(length) {
    console.log(length);
    log(p);
 });

p.call('push', 'banana', Future.error('apple'), 'peach')
 .catch(function(reason) {
    console.log(reason);
 });

执行结果与上面的 apply 的例子执行结果相同。

bind 方法

Future.prototype.bind(method[, arg1[, arg2[, arg3...]]])
Future.prototype.bind(methods[, arg1[, arg2[, arg3...]]])

bind 方法可以将当前 promise 对象所包含的成功值上的一个或多个方法及其参数绑定到当前的 promise 对象上。其中参数可以是 promise 对象,且绑定之后的方法在调用时也支持 promise 对象参数,返回值也为 promise 对象。例如:

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var p = Future.value([]);
p.bind('push');

p.push('banana', Future.value('apple'), 'peach')
 .then(function(length) {
    console.log(length);
    log(p);
 });

p.push('banana', Future.error('apple'), 'peach')
 .catch(function(reason) {
    console.log(reason);
 });

var p2 = Future.value(new Date());
p2.bind(Object.getOwnPropertyNames(Date.prototype));

log(p2.toString());
p2.setFullYear(1980).then(function() {
    log(p2.toString());
});

运行结果如下:

apple
3
Tue Jul 28 2015 20:51:47 GMT+0800 (CST)
[ 'banana', 'apple', 'peach' ]
Mon Jul 28 1980 20:51:47 GMT+0800 (CST)

forEach 方法

Future.prototype.forEach(callback[, thisArg])

该方法是 Array.prototype.forEachpromise 版本。它与 Array.prototype.forEach 的区别跟 Future.forEachArray.forEach 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

function logArrayElements(element, index, array) {
  console.log('a[' + index + '] = ' + element);
}

var p = Future.value([2, Future.value(5), , 9]);
// Note elision, there is no member at 2 so it isn't visited
p.forEach(logArrayElements);

输出结果为:

a[0] = 2
a[1] = 5
a[3] = 9

every 方法

Future.prototype.every(callback[, thisArg])

该方法是 Array.prototype.everypromise 版本。它与 Array.prototype.every 的区别跟 Future.everyArray.every 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isBigEnough(element, index, array) {
  return element >= 10;
}

var a1 = Future.value([12, Future.value(5), 8, Future.value(130), 44]);
var a2 = Future.value([12, Future.value(54), 18, Future.value(130), 44]);
var a3 = Future.value(a1);
var a4 = Future.value(a2);
log(a1.every(isBigEnough));   // false
log(a2.every(isBigEnough));   // true
log(a3.every(isBigEnough));   // false
log(a4.every(isBigEnough));   // true

输出结果为:

false
true
false
true

some 方法

Future.prototype.some(callback[, thisArg])

该方法是 Array.prototype.somepromise 版本。它与 Array.prototype.some 的区别跟 Future.someArray.some 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isBiggerThan10(element, index, array) {
  return element > 10;
}

var a1 = Future.value([2, Future.value(5), 8, Future.value(1), 4]);
var a2 = Future.value([12, Future.value(5), 8, Future.value(1), 4]);
var a3 = Future.value(a1);
var a4 = Future.value(a2);
log(a1.some(isBiggerThan10));   // false
log(a2.some(isBiggerThan10));   // true
log(a3.some(isBiggerThan10));   // false
log(a4.some(isBiggerThan10));   // true

输出结果为:

false
true
false
true

filter 方法

Future.prototype.filter(callback[, thisArg])

该方法是 Array.prototype.filterpromise 版本。它与 Array.prototype.filter 的区别跟 Future.filterArray.filter 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isBigEnough(value) {
  return value >= 10;
}

var p = Future.value([12, Future.value(5), 8, Future.value(130), 44]);
log(p.filter(isBigEnough));

输出结果为:

[ 12, 130, 44 ]

map 方法

Future.prototype.map(callback[, thisArg])

该方法是 Array.prototype.mappromise 版本。它与 Array.prototype.map 的区别跟 Future.mapArray.map 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var p = Future.value([1, Future.value(4), Future.value(9)]);
log(p.map(Math.sqrt));
log(p.map(function(num) {
  return num * 2;
}));

输出结果为:

[ 1, 2, 3 ]
[ 2, 8, 18 ]

reduce 方法

Future.prototype.reduce(callback[, initialValue])

该方法是 Array.prototype.reducepromise 版本。它与 Array.prototype.reduce 的区别跟 Future.reduceArray.reduce 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var p = Future.value([Future.value(0), 1, Future.value(2), 3, Future.value(4)]);

function callback(previousValue, currentValue, index, array) {
  return previousValue + currentValue;
}

log(p.reduce(callback));
log(p.reduce(callback, 10));
log(p.reduce(callback, Future.value(20)));

输出结果为:

10
20
30

reduceRight 方法

Future.prototype.reduceRight(callback[, initialValue])

该方法是 Array.prototype.reduceRightpromise 版本。它与 Array.prototype.reduceRight 的区别跟 Future.reduceRightArray.reduceRight 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function concat(a, b) {
    return a.concat(b);
}

var p = Future.value([[0, 1], Future.value([2, 3]), Future.value([4, 5])]);

log(p.reduceRight(concat, []));
log(p.reduceRight(concat, Future.value([6, 7])));

输出结果为:

[ 4, 5, 2, 3, 0, 1 ]
[ 6, 7, 4, 5, 2, 3, 0, 1 ]

indexOf 方法

Future.prototype.indexOf(searchElement[, fromIndex])

该方法是 Array.prototype.indexOfpromise 版本。它与 Array.prototype.indexOf 的区别跟 Future.indexOfArray.indexOf 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var array = Future.value([1, Future.value(2), Future.value(3)]);

log(array.indexOf(2));
log(array.indexOf(Future.value(3), 1));
log(array.indexOf(1, 1));

输出结果为:

1
2
-1

lastIndexOf 方法

Future.prototype.lastIndexOf(searchElement[, fromIndex])

该方法是 Array.prototype.lastIndexOfpromise 版本。它与 Array.prototype.lastIndexOf 的区别跟 Future.lastIndexOfArray.lastIndexOf 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var array = Future.value([1, Future.value(2), Future.value(3)]);

log(array.lastIndexOf(2));
log(array.lastIndexOf(Future.value(3), 1));
log(array.lastIndexOf(1, 1));

输出结果为:

1
-1
0

includes 方法

Future.prototype.includes(searchElement[, fromIndex])

该方法是 Array.prototype.includespromise 版本。它与 Array.prototype.includes 的区别跟 Future.includesArray.includes 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

var array = Future.value([1, Future.value(2), Future.value(3)]);

log(array.includes(2));
log(array.includes(Future.value(3), 1));
log(array.includes(1, 1));

输出结果为:

true
true
false

find 方法

Future.prototype.find(predicate[, thisArg])

该方法是 Array.prototype.findpromise 版本。它与 Array.prototype.find 的区别跟 Future.findArray.find 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

var array1 = Future.value([4, Future.value(6), 8, Future.value(12)]);
var array2 = Future.value([4, Future.value(5), 7, Future.value(12)]);

log(array1.find(isPrime));
log(array2.find(isPrime));

输出结果为:

undefined
5

findIndex 方法

Future.prototype.findIndex(predicate[, thisArg])

该方法是 Array.prototype.findIndexpromise 版本。它与 Array.prototype.findIndex 的区别跟 Future.findIndexArray.findIndex 的区别相同。

var hprose = require('hprose');
var Future = hprose.Future;

var log = Future.wrap(console.log, console);

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

var array1 = Future.value([4, Future.value(6), 8, Future.value(12)]);
var array2 = Future.value([4, Future.value(5), 7, Future.value(12)]);

log(array1.findIndex(isPrime));
log(array2.findIndex(isPrime));

输出结果为:

-1
1
Clone this wiki locally