简体   繁体   English

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

[英]Why do Promise libraries use event loops?

Considering the following JavaScript code: 考虑以下JavaScript代码:

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

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

In the promise implementations I've seen, promise.resolve() would simply set some property to indicate the promise was resolved and foo() would be called later during an event loop, yet it seems like the promise.resolve() would have enough information to immediately call any deferred functions such as foo(). 在我看过的promise实现中,promise.resolve()只是设置了一些属性来表明promise被解析了,foo()稍后会在事件循环中被调用,但似乎promise.resolve()会有足够的信息可以立即调用任何延迟函数,如foo()。

The event loop method seems like it would add complexity and reduce performance, so why is it used? 事件循环方法似乎会增加复杂性并降低性能,为什么要使用它?

While most of my use of promises is with JavaScript, part of the reason for my question is in implementing promises in very performance intensive cases like C++ games, in which case I'm wondering if I could utilize some of the benefits of promises without the overhead of an event loop. 虽然我对promises的大多数使用都是使用JavaScript,但我的问题的部分原因是在C ++游戏等性能密集的情况下实现承诺,在这种情况下,我想知道是否可以利用promises的一些好处而不使用事件循环的开销。

All promise implementations, at least good ones do that. 所有承诺实施,至少是好的实现。

This is because mixing synchronicity into an asynchronous API is releasing Zalgo . 这是因为将同步性混合到异步API中正在释放Zalgo

The fact promises do not resolve immediately sometimes and defer sometimes means that the API is consistent. 事实承诺有时不会立即解决,延迟有时意味着API是一致的。 Otherwise, you get undefined behavior in the order of execution. 否则,您将按执行顺序获得未定义的行为。

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

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

The fact promise libraries defer, means that the order of execution of the above block is guaranteed. 事实承诺库推迟,意味着保证上述块的执行顺序。 In broken promise implementations like jQuery, the order changes depending on whether or not the item is fetched from the cache or not. 在像jQuery这样的破坏承诺实现中,顺序会根据是否从缓存中提取项目而更改。 This is dangerous. 这很危险。

Having nondeterministic execution order is very risky and is a common source of bugs. 具有非确定性执行顺序是非常危险的,并且是错误的常见来源。 The Promises/A+ specification is throwing you into the pit of success here. Promises / A +规范让你在这里成功。

Whether or not promise.resolve() will synchronously or asynchronously execute its continuations really depends on the implementation. promise.resolve()是否将同步或异步执行其延续实际上取决于实现。

Furthermore, the "Event Loop" is not the only mechanism to provide a different "execution context". 此外,“事件循环”不是提供不同“执行上下文”的唯一机制。 There may be other means, for example threads or thread pools, or think of GCD (Grand Central Dispatch, dispatch lib), which provides dispatch queues. 可能还有其他方法,例如线程或线程池,或者考虑提供调度队列的GCD(Grand Central Dispatch,dispatch lib)。

The Promises/A+ Spec clearly requires that the continuation (the onFulfilled respectively the onRejected handler) will be asynchronously executed with respect to the "execution context" where the then method is invoked. Promises / A + Spec明确要求相对于调用then方法的“执行上下文” 异步执行continuation( onFulfilled onRejected handler)。

  1. onFulfilled or onRejected must not be called until the execution context stack contains only platform code. 在执行上下文堆栈仅包含平台代码之前,不得调用onFulfilledonRejected [3.1]. [3.1]。

Under the Notes you can read what that actually means: 根据Notes,您可以阅读实际含义:

Here "platform code" means engine, environment, and promise implementation code. 这里的“平台代码”意味着引擎,环境和承诺实现代码。 In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. 实际上,这个要求确保onFulfilled和onRejected异步执行,然后调用事件循环,然后调用新堆栈。

Here, each event will get executed on a different "execution context", even though this is the same event loop, and the same "thread". 在这里,每个事件将在不同的“执行上下文”上执行,即使这是相同的事件循环,也是相同的“线程”。

Since the Promises/A+ specification is written for the Javascript environment, a more general specification would simply require that the continuation will be asynchronously executed with respect to the caller invoking the then method. 由于Promises / A +规范是为Javascript环境编写的,因此更通用的规范只需要相对于调用then方法的调用者异步执行continuation。

There are good reasons to this in that way! 这样就有很好的理由!

Example (pseudo code): 示例(伪代码):

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

Assuming, the handler (continuation) will execute on the same thread as the call-site, the order of execution should be that the console shows this: 假设,处理程序(continuation)将在与调用站点相同的线程上执行,执行顺序应该是控制台显示:

acb

Especially, when a promise is already resolved, some implementations tend to invoke the continuation "immediately" (that is synchronously ) on the same execution context. 特别是,当一个promise已经解决时,一些实现倾向于在同一个执行上下文中“立即”(即同步 )调用continuation。 This would clearly violate the rule stated above. 这显然违反了上述规则。

The reason for the rule to invoke the continuation always asynchronously is that a call-site needs to have a guarantee about the relative order of execution of handlers and code following the then including the continuation statement in any scenario. 规则始终异步调用延续的原因是,调用站点需要保证处理程序和代码执行的相对顺序, then包括任何方案中的continuation语句。 That is, no matter whether a promise is already resolved or not, the order of execution of the statements must be the same. 也就是说,无论承诺是否已经解决,声明的执行顺序必须相同。 Otherwise, more complex asynchronous systems may not work reliable. 否则,更复杂的异步系统可能无法可靠运行。

Another bad design choice for implementations in other languages which have multiple simultaneous execution contexts - say a multi-threaded environment (irrelevant in JavaScript, since there is only one thread of execution), is that the continuation will be invoked synchronously with respect to the resolve function. 对于具有多个同时执行上下文的其他语言中的实现的另一个糟糕的设计选择 - 比如说多线程环境(在JavaScript中无关,因为只有一个执行线程),是关于resolve同步调用continuation功能。 This is even problematic when the asynchronous task will finish in a later event loop cycle and thus the continuation will be indeed executed asynchronously with respect to the call-site. 当异步任务将在稍后的事件循环周期中完成时,这甚至是有问题的,因此连续将确实相对于呼叫站点异步执行。

However, when the resolve function will be invoked by the asynchronous task when it is finished, this task may execute on a private execution context (say the "worker thread"). 但是,当异步任务完成时将调用resolve函数时,此任务可以在私有执行上下文(例如“工作线程”)上执行。 This "worker thread" usually will be a dedicated and possibly special configured execution context - which then calls resolve . 这个“工作线程”通常是一个专用的 ,可能是特殊配置的执行上下文 - 然后调用resolve If that resolve function will synchronously execute the continuation, the continuation will run on the private execution context of the task - which is generally not desired. 如果该resolve函数将同步执行continuation,则continuation将在任务的私有执行上下文上运行 - 这通常是不希望的。

Promises are all about cooperative multitasking . 承诺都是关于合作多任务的

Pretty much the only method to achieve that is to use message based scheduling. 实现这一目标的唯一方法是使用基于消息的调度。

Timers (usually with 0 delay) are simply used to post the task/message into message queue - yield-to-next-task-in-the-queue paradigm. 定时器(通常具有0延迟)仅用于将任务/消息发布到消息队列中 - 产生到下一个任务的队列范例。 So the whole formation consisting of small event handlers works and more frequently you yield - more smoothly all this work. 因此,由小事件处理程序组成的整个编队工作,并且更频繁地产生 - 所有这些工作都更顺利。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM