为什么Q.js在解决后承诺异步?

时间:2014-07-22 07:33:10

标签: javascript angularjs promise

如果我有以下内容:

var deferred = Q.defer();

deferred.resolve();

var a = deferred.promise.then(function() {
    console.log(1);    
});

console.log(2); 

...为什么我在控制台中看到2,然后是1?

我理解这个输出是正确的,根据Promises规范,它表示在下一个刻度上调用该函数(例如setTimeout()),即使它已经解决了,但我不明白为什么

我希望代码在一系列承诺上同步调用then,假设所有承诺都已解决。

我真正的用例是我正在尝试使用Angular的实现$q,并希望所有then回调在同一个$digest周期内执行,这样我就不会在接下来的$digest次循环中获得不必要的。

2 个答案:

答案 0 :(得分:2)

答案是一致性。

在实际代码中,您没有承诺始终在创建时立即解决,它们将毫无意义。所以你承诺有时候可以立即解决。

在这种情况下,您不希望拥有不同的流程。你想要总是相同的,可预测的流程。因此,您希望下一个函数在下一个时间点始终调用。

如果您不需要承诺,请不要使用承诺。

答案 1 :(得分:-3)

这是基于一系列观点和假设的设计错误。它之所以被锁定,是因为它在委员会设计的过程中未经充分的技术验证就被赶出了市场,而且还给许多供应商带来了背压,他们已经实施了自己的错误但又难以回溯。

一旦JS的标准发布到网络上,就可以撤销它,即使它被打破了,即认为网页不应该被破坏。如果某人今天写了一个页面然后死了,那么应该可以在五年内在浏览器中查看它。如果您在浏览网络时不断遇到与浏览器不兼容的页面,那将是一个很大的问题。

在非常简单的用例中,它并没有太大危害,并且消除了对于某些事物是否异步的困惑。

对于越来越不平凡的用例,它造成的危害越来越大,并增加了混乱。最初,它似乎使代码推理更容易,但是却牺牲了琐碎的用法,而减少了琐碎的用法。

如果代码在下一滴答中没有运行根本不需要异步的操作,那么总的来说,对代码进行推理就容易得多。这是一种选择损害第二级语言以迎合第一级用户的情况,但要牺牲第二级及以上级别的用户的利益,而不是致力于帮助提升第一级用户到第二级的用户。这是一个居高临下或悲观的设计决定。

有一个中间解决方案,可以像在当前代码运行完成后一样运行每个任务,但可以按正确的顺序安排任务。这还没有实现,而且由于折衷方案并不总是能产生最佳解决方案,因此也值得商bat。这种折衷会产生直接返回调用堆栈的性能问题。

承诺工作的方式意味着前向调用堆栈优先于深度并运行至完成(隔离),而返回调用堆栈则优先于广度并与其他返回调用堆栈交错运行。这是两个截然不同的概念和行为。传统上,回调函数都以相同的方式运行。

这还意味着您不能天真地用promise或任何基于promise的回调代替回调。回调为您提供了更多的承诺,而这些承诺将不复存在。如果用promise替换回调而没有考虑到这种差异,则可以创建具有稳定性问题和潜在安全性问题的代码,因为这种无序事件的行为会导致当前代码流意外跳转。

您不能依靠订单,这意味着在某些情况下,如果您要在退货时要求很多东西,那么您必须仔细检查它们是您要求的东西,因为您不需要使用回调执行此操作。您可能还需要对事件进行缓冲和重新排序,而这些事件不需要使用回调。如果您不小心将正在运行的两件事隔离开,它也会使基准测试不可靠。

这也可能会造成严重的性能瓶颈,而您通常无法轻松地防止这些瓶颈。如果您使用promise从单个事件一次返回给迭代器的一百个返回结果,每个返回调用花费一秒钟,并且它们的promise解析深度为2,那么在前半部分全部运行的情况下,它们将全部分成一半下半场。这意味着要花50.5秒才能完成任何事情,而50秒后的回调就已经完成了一半。如果将任务的结果传递给另一个外部服务进行处理,则当该服务可能正在处理您的结果时,它将使该服务保持空闲状态50秒钟。当您希望在承受负载的服务中同时显示低延迟和高吞吐量时,这使承诺变得可怕。

无法用诺言天真地替换回调是此设计错误最具有破坏性的后果之一,该设计错误也被延续到异步/等待中。如果要转换回调库,就不能简单地更改语法,则必须在每一点上仔细检查语义。

没有解决此问题的计划。您可以创建自己的Promise,并且生成器可用于提供与async / await相同的语法,但具有与回调相同的可预测和高性能行为。但是,在与仍然依赖本机Promise的其他库配合使用时,您可能会遇到问题。