24.14 两个有用的 Promise 附加方法
本节介绍两个很有用的方法,这两个方法可以很容易的添加到 ES6 的 Promise 上面去。很多更全面的 Promise 库都有这两个方法。
24.14.1 done()
当你链接若干个 Promise 方法调用的时候,有一不小心忘记处理某些错误的风险。例如:
function doSomething() {
asyncFunc()
.then(f1)
.catch(r1)
.then(f2); // (A)
}
如果在行 A 的 then()
触发了拒绝( rejection ),那么这个错误将不会被处理。 Promise 库 Q 提供了一个方法 done()
,被用作链式调用的最后一个调用元素。 done()
要么替换掉最后一个 then()
(此时有一到两个参数):
function doSomething() {
asyncFunc()
.then(f1)
.catch(r1)
.done(f2);
}
要么放在最后一个 then()
的后面(此时没有参数):
function doSomething() {
asyncFunc()
.then(f1)
.catch(r1)
.then(f2)
.done();
}
引用 Q 的文档:
done
和then
使用的黄金规则就是:要么返回 Promise 给其它地方,要么调用done
结束当前 Promise 链(如果这条链在当前位置结束的话)。用catch
来结束 Promise 是不完美的,因为 catch 的处理器自身可能会抛出错误。
下面就是在 ECMAScript 6 中实现 done()
的方式:
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// Throw an exception globally
setTimeout(() => { throw reason }, 0);
});
};
虽然 done
的功能很有用,但是还是没有添加到 ECMAScript 6 中去,因为这种检查可以通过引擎自动实现(正如在调试 Promise 的那一节看到的一样)。
24.14.2 finally()
有时你想执行一些操作而不管是否发生了错误。例如,在操作完一个资源之后做一些清理工作。这就是 Promise 方法 finally
适用的场景,它和异常处理中的 finally 子句很相似。它的回调函数不接收参数,但是不管是 rejected 还是 resolved 状态都会被调用。
createResource(···)
.then(function (value1) {
// Use resource
})
.then(function (value2) {
// Use resource
})
.finally(function () {
// Clean up
});
下面是 Domenic Denicola
提议的实现 finally()
的方式:
Promise.prototype.finally = function (callback) {
let P = this.constructor;
// We don’t invoke the callback in here,
// because we want then() to handle its exceptions
return this.then(
// Callback fulfills => continue with receiver’s fulfillment or rejection
// Callback rejects => pass on that rejection (then() has no 2nd paramet\
er!)
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
回调函数决定了接收器( this )的稳定状态处理的方式:
- 如果回调函数抛出异常或者返回一个 rejected 状态的 Promise ,则会变成拒绝值( rejection value )。
- 否则,接收器的稳定状态( fulfilled 或者 rejected )变成了
finally()
返回的 Promise 的稳定状态。在某种程度上,我们把finally
从方法链上脱离出来了。
示例1(作者 Jake Archibald )使用 finally()
隐藏一个 spinner 。简单的版本:
showSpinner();
fetchGalleryData()
.then(data => updateGallery(data))
.catch(showNoDataError)
.finally(hideSpinner);
示例2(作者 Kris Kowal )使用 finally()
拆分一个测试:
let HTTP = require("q-io/http");
let server = HTTP.Server(app);
return server.listen(0)
.then(function () {
// run test
})
.finally(server.stop);