Javascript 递归对Firefox来说是昂贵的

Javascript 递归对Firefox来说是昂贵的,javascript,performance,firefox,canvas,pixel-manipulation,Javascript,Performance,Firefox,Canvas,Pixel Manipulation,编辑:仅在Firefox中出现!(我使用的是22.0)查看底部的浏览器比较 我试图通过复制像素数据并逐步将alpha值从255更改为0(背景为黑色),在画布上创建“淡入黑色”效果 奇怪的是,如果我只是延迟第二次迭代的fadeToBlack(像素操作的第一次迭代) 这到底是怎么回事 编辑:我在几个浏览器中进行了测试,下面是所有15次迭代的结果(以毫秒为单位) Browser |递归|异步 =========+===========+============ Firefox | 1652†| 113

编辑:仅在Firefox中出现!(我使用的是22.0)查看底部的浏览器比较

我试图通过复制像素数据并逐步将alpha值从255更改为0(背景为黑色),在画布上创建“淡入黑色”效果

奇怪的是,如果我只是延迟第二次迭代的
fadeToBlack
(像素操作的第一次迭代)

这到底是怎么回事

编辑:我在几个浏览器中进行了测试,下面是所有15次迭代的结果(以毫秒为单位)

Browser |递归|异步
=========+===========+============
Firefox | 1652†| 1136
铬| 976 | 978
歌剧院| 12929 | 13855
IE | 855 | 854

†第一次迭代非常昂贵(500ms)。

我认为这会将函数之间的跳跃减少一半,因为您只调用一次,直到函数死亡(使用setTimeout的异步调用时),但是如果您通过从内部调用使用递归,它将在该点停止并跳转到下一个调用,依此类推,直到完成最后一个调用,然后逐渐进行递归,从它停止的行调用上一个函数,继续使用递归返回的值并返回到上一个,我可以看到性能和方法上的差异。我想问一下,它是否会给你同样的结果,我怀疑不会是这样

TL;DR setTimeout:异步调用(独立),递归:同步(依赖)。

图形演示:


我认为这样可以将函数之间的跳跃减少一半,因为在函数死亡之前只调用一次(使用setTimeout进行异步调用时),但是如果通过从内部调用来使用递归,它将在该点停止并跳转到下一个调用,依此类推,直到完成最后一个调用,然后逐渐进行递归,从它停止的行调用上一个函数,继续使用递归返回的值并返回到上一个,我可以看到性能和方法上的差异。我想问一下,它是否会给你同样的结果,我怀疑不会是这样

TL;DR setTimeout:异步调用(独立),递归:同步(依赖)。

图形演示:


这很奇怪,但如果使用setTimeout,则会使函数以异步方式执行,当前函数将继续工作,同时将下一个调用放入队列中,以便在后台运行。这不再是递归,因为您不依赖于它返回的值。我感兴趣的是知道这种提升的原因,是不是当你调用异步调用并结束当前调用,从内存中删除当前的流,并认为它与队列无关?当我问这个问题时,我对异步/递归有点模糊,但现在我已经读了一点了。。。我仍然不知道性能提升来自何方。我想这可能是由于两个函数同时访问相同的5mB数据集(this.imageData)而导致的非常特定于引擎的原因。我可能会在不同的浏览器中做一些测试,看看会发生什么。@CME64完成!看起来绝对像一个引擎怪癖!这很奇怪,但如果使用setTimeout,则会使函数以异步方式执行,当前函数将继续工作,同时将下一个调用放入队列中,以便在后台运行。这不再是递归,因为您不依赖于它返回的值。我感兴趣的是知道这种提升的原因,是不是当你调用异步调用并结束当前调用,从内存中删除当前的流,并认为它与队列无关?当我问这个问题时,我对异步/递归有点模糊,但现在我已经读了一点了。。。我仍然不知道性能提升来自何方。我想这可能是由于两个函数同时访问相同的5mB数据集(this.imageData)而导致的非常特定于引擎的原因。我可能会在不同的浏览器中做一些测试,看看会发生什么。@CME64完成!看起来绝对像一个引擎怪癖!
function fadeToBlack () {
    if(typeof this.recursion === 'undefined' || this.recursion === 0) {
        this.recursion = 1;
        this.imageData = this.ctx.getImageData(0, 0, this.width, this.height);
        this.imageDataArray = this.imageData.data;
        this.pixelCount = this.imageDataArray.length/4;
        this.fadeToBlack();
    }
    else if (this.recursion <= 15){
        console.time('Change alpha ' + this.recursion);
        for (var i = 0; i < this.pixelCount; i++){
            this.imageDataArray[i * 4 + 3] = 255 - 255 / 15 * this.recursion;
        }
        console.timeEnd('Change alpha ' + this.recursion);
        this.ctx.putImageData(this.imageData, 0, 0);
        this.recursion++;
        setTimeout(function(){
            this.fadeToBlack();
        }.bind(this), 50);
    }
    else {
        this.recursion = 0;
    }
};
function fadeToBlack () {
    if(typeof this.recursion === 'undefined' || this.recursion === 0) {
        this.recursion = 1;
        this.imageData = this.ctx.getImageData(0, 0, this.width, this.height);
        this.imageDataArray = this.imageData.data;
        this.pixelCount = this.imageDataArray.length/4;
        //This is the only difference!
        setTimeout(function(){
            this.fadeToBlack();
        }.bind(this), 0);
    }
    else if (this.recursion <= 15){
        console.time('Change alpha ' + this.recursion);
        for (var i = 0; i < this.pixelCount; i++){
            this.imageDataArray[i * 4 + 3] = 255 - 255 / 15 * this.recursion;
        }
        console.timeEnd('Change alpha ' + this.recursion);
        this.ctx.putImageData(this.imageData, 0, 0);
        this.recursion++;
        setTimeout(function(){
            this.fadeToBlack();
        }.bind(this), 50);
    }
    else {
        this.recursion = 0;
    }
};