这是基于一组意见和假设的设计错误。它之所以被锁定,是因为它在委员会设计过程中没有经过全面技术验证就匆匆推出,这也有许多供应商已经实施自己的产品的背压,同样的错误使其难以回溯。
一旦 JS 的标准发布到网络上,它就可以被撤销,即使它被打破的想法是网页不应该被打破。如果今天有人写了一个页面,然后死了,五年后应该可以在你的浏览器中查看它。如果您在浏览网页时不断碰到无法使用您的浏览器的页面,那将是非常有问题的。
在非常简单的用例中,它不会造成太大的伤害,并且消除了关于某些东西是否可能是异步的混淆。
对于越来越重要的用例,它会造成越来越大的伤害并增加混乱。最初,它似乎更容易推理您的代码,但它牺牲了不那么琐碎的用法,换取了最不琐碎的用法。
如果代码在下一个滴答中不运行根本不需要异步的东西,那么总体上推理您的代码要容易得多。这是一个选择破坏二级语言以迎合一级用户的情况,以牺牲二级及以上用户为代价,而不是努力帮助一级用户提升到二级用户。这是一个居高临下或悲观的设计决定。
有一个中间解决方案可以运行每个任务,就好像它在当前代码运行完成之后运行一样,但事情是按照正确的顺序安排的。这还没有实施,也有争议,因为妥协并不总是产生最好的解决方案。这种折衷会导致直接返回调用堆栈的性能问题。
promise 的工作方式意味着前向调用堆栈是深度优先并运行到完成(隔离),但返回调用堆栈是广度优先并与其他返回调用堆栈交错运行。这是两种截然不同的概念和行为。传统上,回调都以相同的方式运行。
这也意味着你不能天真地用承诺或任何基于承诺的东西替换回调。回调为您提供了更多的选项,这些选项被承诺带走。如果您将回调替换为 Promise 而没有考虑到这种差异,您可能会创建具有稳定性问题和潜在安全问题的代码,因为这种无序事件的行为可能会导致当前代码流意外跳轨。
您不能依赖订单,这意味着在某些情况下,如果您要求很多东西,当您取回它们时,您必须仔细检查它们是否是您要求的东西,因为您不需要用回调做到这一点。您可能还需要缓冲和重新排序不需要对回调执行的事件。如果您不小心隔离正在运行的两件事,它也会使基准测试变得不可靠。
这也可能会造成严重的性能瓶颈,您无法始终轻松避免。如果你使用 promises 一次从单个事件返回到迭代器的一百个返回结果,每个返回调用需要一秒钟,并且它们的 promise 解析深度是 2,它们将全部分成两半,前半部分全部运行,然后下半场。这意味着任何事情都需要 50.5 秒才能完成,而 50 秒后的回调已经完成了一半。如果任务的结果被传递给另一个外部服务进行处理,那么它会让该服务闲置 50 秒,而它本来可以处理您的结果。当您希望在承受负载的服务下同时获得低延迟和高吞吐量时,这使得承诺变得很糟糕,这表明了设计的弱点。
不能天真地用 Promise 替换回调是这个设计错误的最具破坏性的后果之一,它也被带到 async/await 中。如果你想转换一个回调库,你不能简单地改变语法,你必须仔细检查每一点的语义。
没有解决此问题的计划。您可以创建自己的 Promise,并且生成器可用于提供与 async/await 相同的语法,但具有与回调相同的可预测和高性能行为。但是,与仍然依赖原生 Promise 的其他库配合使用时,您可能会遇到问题。