Far*_*hat 3 javascript performance google-chrome v8 promise
这是jsperf:http://jsperf.com/promise-vs-callback
回调案例(211 Ops/s):
// async test
var d = deferred;
function getData(callback) {
setTimeout(function() {
callback('data')
}, 0)
}
getData(function(data) {
d.resolve()
})
Run Code Online (Sandbox Code Playgroud)
承诺案例(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()
})
Run Code Online (Sandbox Code Playgroud)
如你所见,承诺更快,但他们有更多的代码.问题是为什么会发生这种情况.
这deferred是由jsperf定义的,以显示它作为异步测试的完成.
看起来神奇的技巧在于铬如何设置最小延迟setTimeout(fn, 0).
我搜索了它,我发现了这个:https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/Hn3GxRLXmR0/XP9xcY_gBPQJ
我引用了重要的部分:
定时器钳位的工作方式是每个任务都有一个相关的定时器嵌套级别.如果任务源自setTimeout()或setInterval()调用,则嵌套级别大于调用setTimeout()的任务的嵌套级别或该setInterval()的最近迭代的任务,否则为零.只有嵌套级别为4或更高时,4ms钳位才适用.在事件处理程序,动画回调或未深度嵌套的计时器的上下文中设置的计时器不受钳位.
在回调的情况下,setTimeout在另一个setTimeout的上下文中递归调用,因此最小超时为4ms.在promise的情况下,setTimeout实际上不是递归调用的,所以最小超时为0(实际上不会是0,因为其他东西也必须运行).
那么我们怎么知道setTimeout是递归调用的呢?好吧,我们可以在jsperf或只使用benchmark.js进行实验:
// async test
deferred.resolve()
Run Code Online (Sandbox Code Playgroud)
这将导致Uncaught RangeError: Maximum call stack size exceeded.这意味着,一旦调用deferred.resolve,测试就会在同一个tick/stack上再次运行.所以在回调的情况下,setTimeout在它自己的调用上下文中被调用并嵌套在另一个setTimeout中,这将把最小超时设置为4ms.
但是在promise的情况下,.then根据promise规范在下一个tick之后调用callback,并且v8 在下一个tick之后不使用setTimeout调用回调.它使用的东西,必须类似于process.nextTick在或的NodeJS setImmediate,而不是setTimeout的.这会将setTimeout嵌套级别重置为0并使setTimeout延迟0ms.
| 归档时间: |
|
| 查看次数: |
1215 次 |
| 最近记录: |