Javascript 为什么在这种情况下,原生承诺似乎比chrome中的回调更快?

Javascript 为什么在这种情况下,原生承诺似乎比chrome中的回调更快?,javascript,performance,google-chrome,promise,v8,Javascript,Performance,Google Chrome,Promise,V8,以下是jsperf: 回调案例(211次操作/秒): 承诺案例(614次操作/秒): 正如您所看到的,promise的速度要快得多,但它们有更多的代码。问题是为什么会发生这种情况 这里,jsperf将定义延迟,将其显示为异步测试的完成。首先,您的基准设计错误。它只测量最小的setTimeout值,而不是回调和承诺之间的性能差异 最小延迟为4ms,因此结果不能超过每秒250次操作。以某种方式调用newpromise可以消除最小的4ms延迟 如果您想度量承诺和回调的差异,就需要消除这种不自然的瓶颈。

以下是jsperf:

回调案例(211次操作/秒):

承诺案例(614次操作/秒):

正如您所看到的,promise的速度要快得多,但它们有更多的代码。问题是为什么会发生这种情况


这里,jsperf将定义
延迟
,将其显示为异步测试的完成。

首先,您的基准设计错误。它只测量最小的
setTimeout
值,而不是回调和承诺之间的性能差异

最小延迟为
4ms
,因此结果不能超过每秒250次操作。以某种方式调用
newpromise
可以消除最小的4ms延迟

如果您想度量承诺和回调的差异,就需要消除这种不自然的瓶颈。因此,您不仅在并发级别1上进行测量,而且在每次调用之间等待4毫秒

JSPErf并不能使设置并发性变得容易,但这里是concurrency=1000时的示例:


正如Esailija指出的,这与Promise中奇怪的设置超时优化有关。
另请参见使用更快的setTimeout替代方案制作的相同基准:它提供了更多的预期结果,因为它的魔术在于chrome如何为
setTimeout(fn,0)
设置最小延迟

我搜索了一下,发现了这个:

我引述重要部分:

计时器夹紧的工作方式是每个任务都有一个相关的计时器嵌套级别。如果任务源于setTimeout()或setInterval()调用,则嵌套级别比调用setTimeout()的任务或该setInterval()最近一次迭代的任务的嵌套级别大一级,否则为零。4ms夹具仅适用于嵌套级别为4或更高的情况。在事件处理程序、动画回调或未深度嵌套的计时器的上下文中设置的计时器不受限制

在回调情况下,在另一个setTimeout的上下文中递归调用setTimeout,因此最小超时为4ms。 在promise案例中,setTimeout实际上不是递归调用的,因此最小超时为0(实际上不是0,因为其他东西也必须运行)

那么我们如何知道setTimeout是递归调用的呢?我们可以在jsperf中进行实验,也可以使用benchmark.js:

// async test
deferred.resolve()
这将导致
未捕获范围错误:超过最大调用堆栈大小。
这意味着,一旦调用了deferred.resolve,测试将在同一个tick/堆栈上再次运行。因此,在回调情况下,setTimeout在它自己的调用上下文中被调用,并嵌套在另一个setTimeout中,它将最小超时设置为4ms


但是在promise案例中,
。然后根据promise规范和v8,在下一个勾号后调用
回调。它使用的内容必须与nodejs或中的类似,而不是setTimeout。这会再次将setTimeout嵌套级别重置为0,并使setTimeout延迟为0ms。

奇怪,Chrome 32和33的结果不同。我也希望setTimeout的最小延迟为4ms,但随后我做了一个快速测试以确保它不是:
console.time(1);setTimeout(函数(){console.timeEnd(1)},0)将显示1.3ms,不再是4ms。在这种情况下,我并不是真的对测量承诺与回调感兴趣,但我感兴趣的是black magic v8所做的使承诺案例更快的事情。这是我的问题。@FaridNouriNeshat这根本不足以测试最小延迟,请运行以下示例:
var l=500;var prev=Date.now();setTimeout(函数F(){var now=Date.now();console.log(now-prev);prev=now;if(l-->0)setTimeout(F,0)},0)只有前几个值为1,而其余大部分值为4和5。@FaridNouriNeshat有趣的事实是,这家伙编写的库实际上比本机承诺的要快:DAnother有趣的事实是,这家伙编写的库实际上比我所知道的任何其他库都要快,可以管理回调和并发。向《强大的蓝鸟发现》的作者致敬,也解释了为什么第一个值是1ms,其余的是4mman,因为“更多的代码”何时意味着“更少的性能”?有时,为了获得更好的性能,您必须编写大量代码,以确保某些东西按照底层机制的具体情况运行良好。
// async test
var d = deferred;

function getData() {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve('data')
    }, 0);
  })
}

getData().then(function(data) {
  d.resolve()
})
// async test
deferred.resolve()