为什么Promise库使用事件循环?

时间:2014-05-03 17:45:57

标签: javascript c++ performance promise

考虑以下JavaScript代码:

var promise = new Promise();
setTimeout(function() {
    promise.resolve();
}, 10);

function foo() { }
promise.then(foo);

在我看到的promise实现中,promise.resolve()只是设置了一些属性来表示promise被解析了,foo()稍后会在事件循环中被调用,但它看起来像promise.resolve( )将有足够的信息立即调用任何延迟函数,如foo()。

事件循环方法似乎会增加复杂性并降低性能,为什么要使用?

虽然我对promises的大多数使用都是使用JavaScript,但我的问题的部分原因是在C ++游戏等性能密集的情况下实现承诺,在这种情况下我想知道我是否可以利用它的一些好处承诺没有事件循环的开销。

3 个答案:

答案 0 :(得分:9)

所有承诺实施,至少是好的实现。

这是因为将同步性混合到异步API中是releasing Zalgo

事实承诺有时不会立即解决,而延迟有时意味着API是一致的。否则,您将按执行顺序获得未定义的行为。

function getFromCache(){
      return Promise.resolve(cachedValue || getFromWebAndCache());
}

getFromCache().then(function(x){
     alert("World");
});
alert("Hello");

事实承诺库推迟,意味着保证上述块的执行顺序。在像jQuery这样的破坏承诺实现中,顺序会根据是否从缓存中提取项目而更改。这很危险。

具有非确定性执行顺序是非常危险的,并且是错误的常见来源。 Promises / A +规范让你在这里成功。

答案 1 :(得分:6)

promise.resolve()是否同步或异步执行其延续实际上取决于实现。

此外,“事件循环”不是提供不同“执行上下文”的唯一机制。可能还有其他方法,例如线程或线程池,或者考虑提供调度队列的GCD(Grand Central Dispatch,dispatch lib)。

The Promises/A+ Spec明确要求继续(onFulfilledonRejected处理程序)将相对于“执行上下文”执行异步执行<{1}}方法被调用。

  
      在执行上下文堆栈仅包含平台代码之前,不得调用
  1. thenonFulfilled。 [3.1]。
  2.   

在Notes下,您可以阅读实际含义:

  

此处“平台代码”表示引擎,环境和承诺实现代码。在实践中,这个要求确保onFulfilled和onRejected异步执行,在事件循环转入之后调用,并使用新堆栈。

这里,每个事件都将在不同的“执行上下文”上执行,即使这是相同的事件循环,也是相同的“线程”。

由于Promises / A +规范是为Javascript环境编写的,因此更通用的规范只需要对调用者调用onRejected方法执行异步执行。

这样就有很好的理由!

示例(伪代码):

then

假设处理程序(continuation)将在与调用站点相同的线程上执行,执行顺序应该是控制台显示的:

promise = async_task();
printf("a");
promise.then((int result){
    printf("b");
});
printf("c");

特别是,当promise已经解决时,某些实现倾向于在同一个执行上下文中“立即”(即同步)调用continuation。这显然违反了上述规则。

规则始终异步调用连续的原因是,调用站点需要保证acb 之后的处理程序和代码的相对执行顺序任何场景中的延续声明。也就是说,无论承诺是否已经解决,声明的执行顺序必须相同。否则,更复杂的异步系统可能无法可靠运行。

在具有多个同时执行上下文的其他语言中实现的另一个糟糕的设计选择 - 比如一个多线程环境(在JavaScript中无关,因为只有一个执行线程),是将继续调用同步相对于then函数。当异步任务将在稍后的事件循环周期中完成时,这甚至会出现问题,因此对于调用站点,将继续异步

但是,当异步任务完成后将调用resolve函数时,此任务可以在私有执行上下文(例如“工作线程”)上执行。这个“工作线程”通常是专用和可能特殊配置的执行上下文 - 然后调用resolve。如果resolve函数将同步执行继续,则继续将在任务的私有执行上下文上运行 - 这通常是不希望的。

答案 2 :(得分:2)

承诺都是cooperative multitasking

实现这一目标的唯一方法是使用基于消息的调度。

定时器(通常为0延迟)仅用于将任务/消息发布到消息队列中 - 在队列中生成到下一个任务的范例。因此,由小事件处理程序组成的整个编队工作,并且更频繁地产生 - 所有这些工作都更顺利。