首先,没有任何东西(甚至 JavaScript 错误)能阻止 Promise 向你通知它的决议(如果它 决议了的话)。如果你对一个 Promise 注册了一个完成回调和一个拒绝回调,那么 Promise 在决议时总是会调用其中的一个。
当然,如果你的回调函数本身包含 JavaScript 错误,那可能就会看不到你期望的结果,但 实际上回调还是被调用了。后面我们会介绍如何在回调出错时得到通知,因为就连这些错 误也不会被吞掉。
但是,如果 Promise 本身永远不被决议呢?即使这样,Promise 也提供了解决方案,其使用 了一种称为竞态的高级抽象机制:
// 用于超时一个Promise的工具 function timeoutPromise(delay) {
return new Promise( function(resolve,reject){
setTimeout( function(){
reject( "Timeout!" );
}, delay );
} ); }
// 设置foo()超时 Promise.race( [
foo(),
timeoutPromise( 3000 )
]) .then(
function(){
// foo(..)及时完成!
},
// 试着开始foo() // 给它3秒钟
function(err){
// 或者foo()被拒绝,或者只是没能按时完成 // 查看err来了解是哪种情况
} );
关于这个 Promise 超时模式还有更多细节需要考量,后面我们会深入讨论。 很重要的一点是,我们可以保证一个 foo() 有一个输出信号,防止其永久挂住程序。
调用次数过少或过多
根据定义,回调被调用的正确次数应该是 1。“过少”的情况就是调用 0 次,和前面解释过
的“未被”调用是同一种情况。
“过多”的情况很容易解释。Promise 的定义方式使得它只能被决议一次。如果出于某种 原因,Promise 创建代码试图调用 resolve(…) 或 reject(…) 多次,或者试图两者都调用, 那么这个 Promise 将只会接受第一次决议,并默默地忽略任何后续调用。
由于 Promise 只能被决议一次,所以任何通过 then(…) 注册的(每个)回调就只会被调 用一次。
当然,如果你把同一个回调注册了不止一次(比如p.then(f); p.then(f)😉,那它被调用 的次数就会和注册次数相同。响应函数只会被调用一次,但这个保证并不能预防你搬起石 头砸自己的脚。
未能传递参数 / 环境值 Promise 至多只能有一个决议值(完成或拒绝)。
如果你没有用任何值显式决议,那么这个值就是 undefined,这是 JavaScript 常见的处理方 式。但不管这个值是什么,无论当前或未来,它都会被传给所有注册的(且适当的完成或 拒绝)回调。
还有一点需要清楚:如果使用多个参数调用 resovle(…) 或者 reject(…),第一个参数之 后的所有参数都会被默默忽略。这看起来似乎违背了我们前面介绍的保证,但实际上并没 有,因为这是对 Promise 机制的无效使用。对于这组 API 的其他无效使用(比如多次重复 调用 resolve(…)),也是类似的保护处理,所以这里的 Promise 行为是一致的(如果不是 有点令人沮丧的话)。
如果要传递多个值,你就必须要把它们封装在单个值中传递,比如通过一个数组或对象。 对环境来说,JavaScript 中的函数总是保持其定义所在的作用域的闭包,所以它们当然可以继续访问你提供的 环境状态。当然,对于只用回调的设计也是这样,因此这并不是 Promise 特有的优点—— 但不管怎样,这仍是我们可以依靠的一个保证。