可变上下文状态
简单的(但不优雅且容易出错)解决方案是使用更高范围的变量(链中的所有回调都可以访问),并在获得结果值时将结果值写入它们:
function getExample() {
var resultA;
return promiseA(…).then(function(_resultA) {
resultA = _resultA;
// some processing
return promiseB(…);
}).then(function(resultB) {
// more processing
return // something using both resultA and resultB
});
}
人们也可以使用一个(初始为空的)对象来代替许多变量,结果将作为动态创建的属性存储在对象上。
这个解决方案有几个缺点:
Mutable state is ugly, and global variables are evil.
This pattern doesn't work across function boundaries, modularising the functions is harder as their declarations must not leave the shared scope
The scope of the variables does not prevent to access them before they are initialized. This is especially likely for complex promise constructions (loops, branching, excptions) where race conditions might happen. Passing state explicitly, a declarative design that promises encourage, forces a cleaner coding style which can prevent this.
One must choose the scope for those shared variables correctly. It needs to be local to the executed function to prevent race conditions between multiple parallel invocations, as would be the case if, for example, state was stored on an instance.
Bluebird库鼓励使用传递的对象,使用bind()方法将上下文对象分配给承诺链。每个回调函数都可以通过不可用的this关键字访问它。虽然对象属性比变量更容易出现无法检测到的错别字,但该模式相当聪明:
function getExample() {
return promiseA(…)
.bind({}) // Bluebird only!
.then(function(resultA) {
this.resultA = resultA;
// some processing
return promiseB(…);
}).then(function(resultB) {
// more processing
return // something using both this.resultA and resultB
}).bind(); // don't forget to unbind the object if you don't want the
// caller to access it
}
这种方法可以很容易地在不支持.bind的promise库中模拟(尽管以一种更详细的方式,并且不能在表达式中使用):
function getExample() {
var ctx = {};
return promiseA(…)
.then(function(resultA) {
this.resultA = resultA;
// some processing
return promiseB(…);
}.bind(ctx)).then(function(resultB) {
// more processing
return // something using both this.resultA and resultB
}.bind(ctx));
}