Javascript Promise

Promise对象最早被C++工程师使用,接着以Deferred对象出现在Python中。随着NodeJS的兴起,Promise在javascript中的应用越来越广。

大凡技术成熟之后都会逐渐形成一个标准,Promise的标准定义在CommonJS中,全称是Promise/A,不过新近出现了改良版的Promise/A+,只不过它仅仅对原来的标准部分澄清,同时依据实践稍作扩展。

The core Promises/A+ specification does not deal with how to create, fulfill, or reject promises, choosing instead to focus on providing an interoperable(协作的) then method. Future work in companion specifications may touch on these subjects.

但是使用Promise还是有些地方值得留意的。

1. Then的Resovler Function不返回任何值


举个例子,打印deferred数组的首个元素

promise_sample.js
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
28
29
30
31
32
33
34
function printFirstAndLast(itemsDeferred){
findFirst(itemsDeferred).then(util.puts); //=> 0

last(itemsDeferred).then(function(last){
var deferred = defer();
deferred.resolve(last);
findFirst(deferred).then(util.puts) //=> 1
}).then(function(result){
console.log('result:', result); //=> 2
}, function(reason){
console.log('reason:', reason);
});
}

function last(itemsDeferred) {
return itemsDeferred.then(function(items){
return items.slice(1, items.length);
});
}

function findFirst(itemsDeferred){
return itemsDeferred.then(function(items){
return items[0];
});
}
---
var deferred = defer();
deferred.resolve([1, 2, 3, 4, 5]);
printFirstAndLast(deferred);

->
1
result: undefined
2

从输出看,由于我故意没有返回1处的Promise对象,最后一个then 的2处代码先被执行,打印出undefined,然后才会执行完1处的代码,打印出数字2。

这样的现象有两处值得注意的地方

  • 执行顺序混乱
  • 最后then的结果是undefined

顺序混乱是由于标识为1处的resolve的时间较长,回调的时间较久,所以后执行。

2处为undefined,是因为1处没有返回值,我们知道javascript的function始终会返回值且这两种方式:return;(返回void)和不显式写return语句都会返回undefined。所以将值为undefined的promise对象传给下一个then方法。

2. Then的Rejector Function不返回Reject Promise


reject.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function printFirstAndLast(itemsDeferred){
return itemDeferred.then(function(){}, function(reason){
util.puts('1:'+reason);
return 'error-1';
}).then(function(result){
util.puts('resolve 2:'+result);
}, function(reason){
util.puts('reject 2:'+reason);
});
}

var deferred = defer();
deferred.reject('error-0');
printFirstAndLast(deferred);

->
1: error-0
resolve 2:error-1

从输出看,如果reject function不返回reject promise,而只是error-1这样的值,那么调用then方法,执行的还是resolve方法。

从Promise/A+参考描述可证明上述

promise2 = promise1.then(onFulfilled, onRejected);

If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).

If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.

3. Then的Resolver Function返回的是对象,而且包含then方法


obj_then.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

var obj = {then: function(resolver, reject){
return resolver('i am an obj.');
}};

function printFirstAndLast(itemsDeferred){
return itemDeferred.then(function(){}, function(reason){
util.puts('1:'+reason);
return obj; //=> 1
}).then(function(result){
util.puts('resolve 2:'+result);
}, function(reason){
util.puts('reject 2:'+reason);
});
}

var deferred = defer();
deferred.reject('error-0');
printFirstAndLast(deferred);

->
1: error-0
resolve 2:i am an obj

从结果看,obj这个含有then方法的对象,在1处被调用了then,同时将resolver('i am an obj.')作为Promise返回。

从Promise/A+可知

If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:

If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)

If/when rejectPromise is called with a reason r, reject promise with r

那么如果是以下这些对象

  • {then: ‘something’}
  • {then: function(){}}
  • {A: ‘A’}
  • {then: function(resolver, rejector){return rejector(“error”);}}

结果会是

  • resolve 2:[Object Object]
  • 忽略
  • resolve 2:[Object Object]
  • reject 2: error