为什么在这种情况下,native承诺似乎比chrome中的回调更快? [英] Why in this case native promises seems to be faster than callbacks in chrome?

查看:118
本文介绍了为什么在这种情况下,native承诺似乎比chrome中的回调更快?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是jsperf: http://jsperf.com/promise-vs-callback

回调函数(211 Ops / s):

  //异步测试
var d =延期;

function getData(callback){
setTimeout(function(){
callback('data')
},0)
}

getData(函数(数据){
d.resolve()
})

Promise case(614 ops / s):

  //异步测试
var d =延期;
$ b $ function getData(){
return new Promise(function(resolve){
setTimeout(function(){
resolve('data')
},0);
})
}

getData()。then(function(data){
d.resolve()
})

正如你所看到的承诺速度更快,但他们有更多的代码。问题是为什么会发生这种情况。



这里 deferred 是由jsperf定义的,表示它是完成async test。

解决方案

由于看起来魔术技巧在于chrome如何设置 setTimeout(fn,0)



我搜索了它并找到了这个: https://groups.google.com/a/chromium.org/forum/#!msg / blink-dev / Hn3GxRLXmR0 / XP9xcY_gBPQJ

我引用了重要部分:

lockquote

定时器钳位的工作方式是每个任务都有一个关联的定时器嵌套级别。如果任务来自setTimeout()或setInterval()调用,则嵌套级别比调用setTimeout()或该setInterval()的最近一次迭代任务的嵌套级别大1,否则为零。只有在嵌套级别为4或更高时才能应用4ms钳位。












$ b

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

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

  // async test 
deferred。 resolve()

这将导致未捕获的RangeError:超出最大调用堆栈大小。这意味着,一旦deferred.resolve被调用,测试会再次在同一个tick / stack上运行。所以在回调的情况下,setTimeout在它自己的调用上下文中被调用并嵌套在另一个setTimeout中,这会将最小超时设置为4ms。



但是在承诺情况下, code> .then 根据promise规范在下一个tick后调用回调,并且v8 不会使用setTimeout在下一个勾号后调用回调。它使用的内容必须与nodejs中的 process.nextTick 类似或 setImmediate ,而不是setTimeout。它将setTimeout嵌套级别重新设置为0,并使setTimeout延迟0ms。


Here's the jsperf: http://jsperf.com/promise-vs-callback

callback case (211 Ops/s):

// async test
var d = deferred;

function getData(callback) {
  setTimeout(function() {
    callback('data')
  }, 0)
}

getData(function(data) {
  d.resolve()
})

Promise case(614 ops/s):

// async test
var d = deferred;

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

getData().then(function(data) {
  d.resolve()
})

As you see promise are way faster, but they have more code. The question is why this happens.

Here deferred is to defined by jsperf to show it as the completion of the async test.

解决方案

As it seems the magic trick lies with in how chrome sets the minimum delay for setTimeout(fn, 0).

I searched for it and I found this: https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/Hn3GxRLXmR0/XP9xcY_gBPQJ

I quote the important part:

The way timer clamping works is every task has an associated timer nesting level. If the task originates from a setTimeout() or setInterval() call, the nesting level is one greater than the nesting level of the task that invoked setTimeout() or the task of the most recent iteration of that setInterval(), otherwise it's zero. The 4ms clamp only applies once the nesting level is 4 or higher. Timers set within the context of an event handler, animation callback, or a timer that isn't deeply nested are not subject to the clamping.

In the callback case, setTimeout is called recursively, in a context of another setTimeout , so the minimum timeout is 4ms. In the promise case, setTimeout is actually not called recursively, so the minimum timeout is 0(It wouldn't be actually 0, because other stuff has to run too).

So how do we know setTimeout is called recursively? well we can just conduct an experiment in jsperf or just using benchmark.js:

// async test
deferred.resolve()

Which will result in Uncaught RangeError: Maximum call stack size exceeded. Which means, once deferred.resolve is called, the test is run again on the same tick/stack. So in the callback case setTimeout is called in it's own calling context and nested in another setTimeout, which will set the minimum timeout to 4ms.

But in the promise case, .then callback is called after the next tick according to promise spec, and v8 doesn't use setTimeout calling the callback after the next tick. It uses something that must be similar to process.nextTick in nodejs or setImmediate, and not setTimeout. Which resets the setTimeout nesting level to 0 again and makes the setTimeout delay 0ms.

这篇关于为什么在这种情况下,native承诺似乎比chrome中的回调更快?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆