Dav*_*ong 17 javascript asynchronous promise ecmascript-6 es6-promise
假设我们有一个返回承诺3项异步任务:A,B和C.我们希望将它们链接在一起(也就是说,为了清楚起见,获取返回的值A并B使用它调用),但是也希望正确处理每个错误,并在第一次失败时突破.目前,我看到了两种方法:
A
.then(passA)
.then(B)
.then(passB)
.then(C)
.then(passC)
.catch(failAll)
Run Code Online (Sandbox Code Playgroud)
这里,passX函数处理每次调用成功X.但在failAll功能,我们不得不处理所有的错误A,B并且C,这可能是复杂的,不容易读,特别是如果我们有超过3个异步任务.所以另一种方式考虑到这一点:
A
.then(passA, failA)
.then(B)
.then(passB, failB)
.then(C)
.then(passC, failC)
.catch(failAll)
Run Code Online (Sandbox Code Playgroud)
在这里,我们分离出的原始的逻辑failAll为failA,failB和failC,这看似简单易读,因为所有的错误都紧挨着它的源处理.但是,这不符合我的要求.
让我们看看是否A失败(拒绝),failA不得继续调用B,因此必须抛出异常或拒绝调用.但是,这两个被抓的failB和failC,也就是说,failB和failC需要知道,如果我们已经失败与否,大概是通过保持状态(即变量).
而且,似乎我们拥有的异步任务越多,要么我们的failAll函数在大小上增长(方式1),要么failX调用更多函数(方式2).这让我想到了我的问题:
有一个更好的方法吗?
考虑:由于异常then是由拒绝方法处理的,是否应该有一种Promise.throw实际中断链的方法?
一个可能的重复,答案可以在处理程序中添加更多范围.承诺是不是应该遵循函数的线性链接,而不是传递传递函数的函数的函数?
jfr*_*d00 10
你有几个选择.首先,让我们看看我是否可以提炼您的要求.
您希望在错误发生的位置附近处理错误,因此您没有一个错误处理程序,必须对所有可能的不同错误进行排序以查看要执行的操作.
当一个承诺失败时,您希望能够中止链的其余部分.
一种可能性是这样的:
A().then(passA).catch(failA).then(val => {
return B(val).then(passB).catch(failB);
}).then(val => {
return C(val).then(passC).catch(failC);
}).then(finalVal => {
// chain done successfully here
}).catch(err => {
// some error aborted the chain, may or may not need handling here
// as error may have already been handled by earlier catch
});
Run Code Online (Sandbox Code Playgroud)
然后,在每一个failA,failB,failC,你会得到该步骤的具体错误.如果要中止链,则在函数返回之前重新抛出.如果您希望链继续,您只需返回正常值.
上面的代码也可以像这样编写(如果passB或passC抛出或返回被拒绝的承诺,行为略有不同).
A().then(passA, failA).then(val => {
return B(val).then(passB, failB);
}).then(val => {
return C(val).then(passC, failC);
}).then(finalVal => {
// chain done successfully here
}).catch(err => {
// some error aborted the chain, may or may not need handling here
// as error may have already been handled by earlier catch
});
Run Code Online (Sandbox Code Playgroud)
由于这些都是完全重复的,因此您可以使整个事物在任何长度的序列中都是表驱动的.
function runSequence(data) {
return data.reduce((p, item) => {
return p.then(item[0]).then(item[1]).catch(item[2]);
}, Promise.resolve());
}
let fns = [
[A, passA, failA],
[B, passB, failB],
[C, passC, failC]
];
runSequence(fns).then(finalVal => {
// whole sequence finished
}).catch(err => {
// sequence aborted with an error
});
Run Code Online (Sandbox Code Playgroud)
链接大量承诺的另一个有用点是,如果为每个拒绝错误创建一个唯一的Error类,那么如果您需要知道哪个步骤导致了中止链,您可以更容易地instanceof在最终.catch()处理程序中使用错误类型.像Bluebird这样的库提供了特定的.catch()语义,使得.catch()它只能捕获特定类型的错误(就像try/catch这样做的方式).您可以在此处查看Bluebird如何做到这一点:http://bluebirdjs.com/docs/api/catch.html.如果您要根据自己的承诺拒绝处理每个错误(如上例所示),那么除非您仍然需要知道最后.catch()一步哪个步骤导致错误,否则不需要这样做.
我推荐两种方式(取决于你想要用这个来完成的):
是的,您希望使用单个catch来处理promise链中的所有错误.
如果您需要知道哪一个失败,您可以使用如下的唯一消息或值拒绝承诺:
A
.then(a => {
if(!pass) return Promise.reject('A failed');
...
})
.then(b => {
if(!pass) return Promise.reject('B failed');
...
})
.catch(err => {
// handle the error
});
Run Code Online (Sandbox Code Playgroud)
或者,您可以在其中返回其他承诺 .then
A
.then(a => {
return B; // B is a different promise
})
.then(b => {
return C; // C is another promise
})
.then(c => {
// all promises were resolved
console.log("Success!")
})
.catch(err => {
// handle the error
handleError(err)
});
Run Code Online (Sandbox Code Playgroud)
在每个承诺中,您将需要某种独特的错误消息,以便您知道哪一个失败.
由于这些是箭头功能,我们可以删除括号!我喜欢的另一个原因是承诺
A
.then(a => B)
.then(b => C)
.then(c => console.log("Success!"))
.catch(err => handleError(err));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8022 次 |
| 最近记录: |