q.allsettled
我最近阅读了v8博客中的Promise组合器文章 。 它涉及Promise API中即将推出的两个方法: Promise.allSettled()和Promise.any() 。 我感到沮丧。 在我看来,这些方法的设计与当前的Promise API不一致。 让我在下面分享我的意见。
承诺全部解决
根据文章:
Promise.allSettled会在所有输入承诺均已兑现时向您发出信号,这意味着它们已实现或被拒绝。
用例是发送几个API调用并等待所有完成:
const promises = [
fetch( '/api-call-1' ),
fetch( '/api-call-2' ),
fetch( '/api-call-3' ),
];
await Promise .allSettled(promises);
removeLoadingIndicator();
当然,这很有用。 但是可以使用.map()和Promise.all()轻松解决此任务。 变化很小:
const promises = [
fetch( '/api-call-1' ),
fetch( '/api-call-2' ),
fetch( '/api-call-3' ),
].map( p => p.catch( e => e)); // <-- the only change
await Promise .all(promises);
removeLoadingIndicator();
是否值得实施可以用几行代码解决的新核心方法? 对我来说,这是一个库功能,而不是核心API方法。
但是更重要的是Promise.allSettled带来了额外的抽象级别。 与Promise.all不同,它使用包装对象数组{status,reason}代替纯Promise值来实现。 作为开发人员,我不喜欢它。 我希望名称类似.all()/。allSettled()的方法的行为类似。 但是他们没有。
而且,带有Promise.allSettled的代码鼓励后期错误处理。 错误应该从最终结果中滤除,而不是从传统的catch块中滤除。 反过来,这具有以下缺点:
- 错误发生时不会立即进行处理。 如果发生几个相关的错误,您可能不知道哪个是原始错误。 并且日志将包含不正确的时间戳。
- 如果至少一个诺言永远悬而未决,则不会处理错误。
使用当前的Promise.all,您可以避免此类情况。
无极
承诺之一兑现后,Promise.any就会向您发出信号。
换句话说, Promise.any是Promise.race ,它会忽略拒绝。
用例是检查多个端点并从第一个成功的端点获取数据:
const promises = [
fetch( '/endpoint-a' ).then( () => 'a' ),
fetch( '/endpoint-b' ).then( () => 'b' ),
fetch( '/endpoint-c' ).then( () => 'c' ),
];
try {
const first = await Promise .any(promises);
} catch (error) {
// All of the promises were rejected.
console .log(error);
}
我同意有时它可能有用。 但是多久一次? 您在多少个项目中使用了“ 对相同数据的相同端点发出多个并行请求”模式 ? 随时分享评论。 但是从我的角度来看-并不经常。 对于社区而言,获取蓝鸟的 Promise.each()或Promise.delay()的本地实现不是更有用吗?
此外, Promise.any引入了一种新的错误类型— AggregateError 。 如果所有承诺均被拒绝,则此类错误包含其他错误的链接。 另一种错误处理方法! 它不同于Promise.allSettled ,后者从成功结果中提取错误。 它也不同于Promise.all / Promise.race ,后者仅通过Error实例拒绝。 如果每个新的Promise API方法都将引入新的错误处理方式,那么JavaScript将如何? 尽管该提案尚处于初期阶段,但我对该方向感到担忧。
基于当前的Promise API, Promise.any的实现有些棘手,但实际上有两行代码 :
const reverse = p => new Promise ( ( resolve, reject ) => Promise .resolve(p).then(reject, resolve));
Promise .any = arr => reverse( Promise .all(arr.map(reverse)));
也许将其保存在图书馆?
前后矛盾
为什么Promise.all和Promise.race如此漂亮?
因为它们的行为非常一致,并且与通常的诺言相似:只需执行一个值即可完成,而仅执行错误即可拒绝。 没有包装的值,没有累积的错误,没有额外的复杂性。
为什么Promise.allSettled和Promise.any对我这么奇怪?
Promise.allSettled使用对象数组来实现,该对象数组具有包装潜在承诺值的状态和原因。 拒绝...永远。
Promise.any满足单个值,但忽略中间拒绝。 仅当所有诺言都被拒绝时,它才会以累积原因包装所有根本原因来拒绝。
这些新方法真的很难让我想到。 由于它们与当前的Promise API完全不同。
我想在2020年出现一个受欢迎的工作面试问题:
这四种方法有什么区别?
- Promise.all()
- Promise.allSettled()
- Promise.race()
- Promise.any()
尽管这是一个很酷的问题,但我认为核心API不应鼓励这种复杂性。
命名
我也对命名感到失望。 四种行为稍有不同的方法应使用非常清晰的名称。 否则,每次在代码中遇到MDN时,我都必须重新检查MDN 。 从Promise.any的建议中:
它清楚地描述了它的作用
让我不同意。 对我来说Promise.any的名称令人困惑:
- 如果有任何诺言,诺言就会实现吗? 是的 。
- 如果任何承诺被拒绝,它会拒绝吗? 不行
- 如果有任何诺言,它会解决吗? 这要看情况 。
- 它与Promise.race有何不同? 嗯 ..
我认为,每个方法的名称都应在方法实现时明确定义条件。 我建议以下命名约定:
Promise .all -> Promise .allFulfilled
Promise .allSettled -> Promise .allSettled
Promise .race -> Promise .oneSettled
Promise .any -> Promise .oneFulfilled
它反映了承诺状态的四种可能组合。 它解释了为什么在建议中将这些方法称为组合器 。
当然,这样的重命名是不可能的,因为Promise.all和Promise.race已经登陆并在许多应用程序中使用了。 但是对于具有某些命名策略的新方法将非常有帮助。
我已经在GitHub上的Promise.any提案存储库中打开问题 ,欢迎您分享您的想法。
吞下拒绝
总的来说,我不受非扔掉的“吞咽”拒绝概念的启发。 实际上,新的Promise API提供了一种方法来静默忽略代码中的错误:
- Promise.allSettled从不拒绝。
- Promise.any仅在所有承诺均被拒绝的情况下拒绝。
当前没有其他核心JavaScript API可以做到这一点。 忽略错误的唯一方法-手动将其包装到带有空主体的try..catch / .catch()中 。 并发表评论,为什么在这里忽略错误,否则eslint会警告您 。
我认为核心API应该公开所有错误 。 是否处理错误始终是开发人员的决定。 对于其他开发人员来说,阅读代码应该是明确的。 试想一下,由于吞咽拒绝的使用不正确,调试将花费多少小时! 尤其是在处理第三方代码时-当某些操作不起作用并且没有引发任何错误时。
结论
我在每个工作日都使用诺言。 和许多其他开发人员一样。 我喜欢JavaScript的异步特性。 拥有清晰直观的API,可让我更快地解决任务并提高工作效率。 这就是为什么我认为应该非常谨慎地对待和更改Promise API的原因。
感谢您的阅读,欢迎发表评论。
翻译自: https://hackernoon.com/whats-wrong-with-promiseallsettled-and-promiseany-yfib64aiv
q.allsettled