why does this setTimeout() call work in the console but not as a greasemonkey script?

余生长醉 提交于 2019-12-09 03:29:04

问题


When I use a setTimeout() in a for() loop in a greasemonkey script, it doesn't appear to work at all. However, the exact same code works fine if I run it in the Firebug console. Here's the code:

// ==UserScript==
// @name           setTimeout test
// @include        *
// @run-at         document-end
// ==/UserScript=


function test(delaytime) {
    alert("test called with "+delaytime);
}

function test2() {
  for( var i = 0; i < 100; i+= 10 ) {
    setTimeout('test('+i+');', i);
  }
}

setTimeout(test2,10);

If I replace the for() loop with explicit calls like the following, then it works fine.

setTimeout(function() { test( 0); },  0);
setTimeout(function() { test(10); }, 10);
setTimeout(function() { test(20); }, 20);
setTimeout(function() { test(30); }, 30);
setTimeout(function() { test(40); }, 40);
setTimeout(function() { test(50); }, 50);
setTimeout(function() { test(60); }, 60);
setTimeout(function() { test(70); }, 70);
setTimeout(function() { test(80); }, 80);
setTimeout(function() { test(90); }, 90);

What's the difference? Is there any way I can get the for loop generated setTimeouts to work in greasemonkey?


回答1:


Because when the string is evaled at the time the setTimeout fires in order to execute the function, the loop has run it's course and i is sitting at the last value of the loop.

To freeze the value of i for each call to setTimeout, you need to capture it in a function closure like this:

function test2() {
  for( var i = 0; i < 100; i+= 10 ) {
    setTimeout(function(val) {
        return(function() {test(val);});
    } (i), i);
  }
}

This also has the advantage of getting rid of the eval in the setTimeout parameter.




回答2:


You have to duplicate the value of i into a locally scoped variable:

function test2() {
  for( var i = 0; i < 100; i+= 10 ) {
    (function(i){
        setTimeout('test('+i+');', i);
    })(i);
  }
}

On a side note: You should pass an anonymous function to setTimeout instead of passing it a string (which will be eval'ed). It's much faster this way:

function test2() {
    for( var i = 0; i < 100; i+= 10 ) {
        (function(i){
            setTimeout(function(){
                test(i);
            }, i);
        })(i);
    }
}


来源:https://stackoverflow.com/questions/7356976/why-does-this-settimeout-call-work-in-the-console-but-not-as-a-greasemonkey-sc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!