我想使用promises将调用链接到不同的库。如果失败,库方法将返回描述错误的对象,但具有不同的字段,具体取决于库。
为了向被调用者一致地报告任何错误,我想规范化所有错误对象以遵循通用格式。但我不知道如何使用Bluebird和/或标准的promise API以优雅的方式做到这一点。
在伪js中,这就是我所拥有的:
var methodAFromLibX_Async = Promise.promisify(...);
var methodBFromLibY_Async = Promise.promisify(...);
methodAFromLibX_Async(...)
.then(function(result) {
methodBFromLibY_Async(...)
.then(function(result) { ... })
.catch(normalizeAndSendErrorFromLibY);
})
.catch(normalizeAndSendErrorFromLibX);
上面的代码似乎可以工作,但是:
答案 0 :(得分:1)
如果你想避免你在你的例子中展示的模式,似乎还有两个其他选择:
您promisify
所显示的库,在您的链中正确传播错误,然后构建一个能够规范化所有已知错误的函数:
var methodAFromLibX_Async = Promise.promisify(...);
var methodBFromLibY_Async = Promise.promisify(...);
methodAFromLibX_Async(...)
.then(function(result) {
return methodBFromLibY_Async(...)
.then(function(result) { ... })
})
.catch(function(err){
if (hasLibXShape(err)){
return Promise.reject(normalizeLibXErr(err));
} else if (hasLibYShape(err)){
return Promise.reject(normalizeLibYErr(err));
}
})
.catch(function(normalizedErr){
// handle normalized error
});
另一种选择是手动宣传您的库方法并在此处规范化错误:
function promisifiedMethodA(/*...args*/){
var args = [].slice.call(arguments);
return new Promise(function(resolve, reject){
methodA.apply(null, args.concat([function(err, data){
if (err) return reject(normalizeMethodAError(err));
resolve(data);
}]));
});
}
阅读您的最新评论我猜后者可能更适合您的需求。
答案 1 :(得分:1)
您可以使用bluebird过滤捕获:http://bluebirdjs.com/docs/api/catch.html#filtered-catch
并将您的代码更改为以下内容:
var methodAFromLibX_Async = Promise.promisify(...);
var methodBFromLibY_Async = Promise.promisify(...);
methodAFromLibX_Async(...)
.then(function(result) {
return methodBFromLibY_Async(...);
}).then(function(result) {
...
}).catch({code: 'X_Async', message: 'bad lib X'}, function(e) {
//If it is a methodAFromLibX_AsyncError, will end up here because
}).catch({code: 'Y_Async', message: 'bad lib Y'}, function(e) {
//Will end up here if a was never declared at all
}).catch(function(e) {
//Generic catch-the rest, error wasn't methodAFromLibX_AsyncError nor
//methodBFromLibY_AsyncError
});
答案 2 :(得分:1)
您正在寻找的解决方案是
return methodAFromLibX_Async(…)
.then(function(result) {
return methodBFromLibY_Async(…)
.then(function(result) {
return methodCFromLibX_Async(…)
.catch(normalizeAndThrowErrorFromLibX);
}, normalizeAndThrowErrorFromLibY);
}, normalizeAndThrowErrorFromLibX)
.then(reportSuccess, reportError);
但这非常难看。鉴于您的错误处理程序无论如何都会重新抛出错误,您也可以使用
return methodAFromLibX_Async(…)
.catch(normalizeAndThrowErrorFromLibX)
.then(function(result) {
return methodBFromLibY_Async(…)
.catch(normalizeAndThrowErrorFromLibY)
.then(function(result) {
return methodCFromLibX_Async(…)
.catch(normalizeAndThrowErrorFromLibX);
});
})
.then(reportSuccess, reportError);
仍然不是最佳的。您不希望在每次调用这些函数时放置.catch(normalise)
,并且您不希望被强制嵌套它们。因此,在他们自己的功能中更好地考虑每个因素:
function withRejectionHandler(fn, normalise) {
return function() {
return fn.apply(this, arguments).catch(normalise);
};
}
var methodA = withRejectionHandler(methodAFromLibX_Async, normalizeAndThrowErrorFromLibX);
var methodB = withRejectionHandler(methodBFromLibY_Async, normalizeAndThrowErrorFromLibY);
var methodA = withRejectionHandler(methodCFromLibX_Async, normalizeAndThrowErrorFromLibX);
return methodA(…).then(methodB).then(methodC).then(reportSuccess, reportError);
您可以将其与图书馆方法的宣传结合起来。
答案 3 :(得分:0)
作为替代解决方案,使用Bluebird的Promise.coroutine:
/* Bergi's solution to normalize */
function withRejectionHandler(fn, normalise) {
return function() {
return fn.apply(this, arguments).catch(normalise);
};
}
var methodA = withRejectionHandler(methodAFromLibX_Async, normalizeAndThrowErrorFromLibX);
var methodB = withRejectionHandler(methodBFromLibY_Async, normalizeAndThrowErrorFromLibY);
var methodA = withRejectionHandler(methodCFromLibX_Async, normalizeAndThrowErrorFromLibX);
/* use of coroutine to sequence the work */
var workflow = Promise.coroutine(function*() {
var resA = yield methodA(...);
var resB = yield methodB(...);
var resC = yield methodC(...);
reportSuccess(resA, resB, resC);
});
workflow().catch(reportError);