How can I pass a parameter to a setTimeout() callback?

前端 未结 28 1904
既然无缘
既然无缘 2020-11-21 07:31

I have some JavaScript code that looks like:

function statechangedPostQuestion()
{
  //alert("statechangedPostQuestion");
  if (xmlhttp.readyState==         


        
相关标签:
28条回答
  • 2020-11-21 07:55

    In general, if you need to pass a function as a callback with specific parameters, you can use higher order functions. This is pretty elegant with ES6:

    const someFunction = (params) => () => {
      //do whatever
    };
    
    setTimeout(someFunction(params), 1000);
    

    Or if someFunction is first order:

    setTimeout(() => someFunction(params), 1000); 
    
    0 讨论(0)
  • 2020-11-21 07:55

    setTimeout is part of the DOM defined by WHAT WG.

    https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html

    The method you want is:—

    handle = self.setTimeout( handler [, timeout [, arguments... ] ] )

    Schedules a timeout to run handler after timeout milliseconds. Any arguments are passed straight through to the handler.

    setTimeout(postinsql, 4000, topicId);
    

    Apparently, extra arguments are supported in IE10. Alternatively, you can use setTimeout(postinsql.bind(null, topicId), 4000);, however passing extra arguments is simpler, and that's preferable.

    Historical factoid: In days of VBScript, in JScript, setTimeout's third parameter was the language, as a string, defaulting to "JScript" but with the option to use "VBScript". https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa741500(v%3Dvs.85)

    0 讨论(0)
  • 2020-11-21 07:58

    This is a very old question with an already "correct" answer but I thought I'd mention another approach that nobody has mentioned here. This is copied and pasted from the excellent underscore library:

    _.delay = function(func, wait) {
      var args = slice.call(arguments, 2);
      return setTimeout(function(){ return func.apply(null, args); }, wait);
    };
    

    You can pass as many arguments as you'd like to the function called by setTimeout and as an added bonus (well, usually a bonus) the value of the arguments passed to your function are frozen when you call setTimeout, so if they change value at some point between when setTimeout() is called and when it times out, well... that's not so hideously frustrating anymore :)

    Here's a fiddle where you can see what I mean.

    0 讨论(0)
  • 2020-11-21 07:58

    I recently came across the unique situation of needing to use a setTimeout in a loop. Understanding this can help you understand how to pass parameters to setTimeout.

    Method 1

    Use forEach and Object.keys, as per Sukima's suggestion:

    var testObject = {
        prop1: 'test1',
        prop2: 'test2',
        prop3: 'test3'
    };
    
    Object.keys(testObject).forEach(function(propertyName, i) {
        setTimeout(function() {
            console.log(testObject[propertyName]);
        }, i * 1000);
    });
    

    I recommend this method.

    Method 2

    Use bind:

    var i = 0;
    for (var propertyName in testObject) {
        setTimeout(function(propertyName) {
            console.log(testObject[propertyName]);
        }.bind(this, propertyName), i++ * 1000);
    }
    

    JSFiddle: http://jsfiddle.net/MsBkW/

    Method 3

    Or if you can't use forEach or bind, use an IIFE:

    var i = 0;
    for (var propertyName in testObject) {
        setTimeout((function(propertyName) {
            return function() {
                console.log(testObject[propertyName]);
            };
        })(propertyName), i++ * 1000);
    }
    

    Method 4

    But if you don't care about IE < 10, then you could use Fabio's suggestion:

    var i = 0;
    for (var propertyName in testObject) {
        setTimeout(function(propertyName) {
            console.log(testObject[propertyName]);
        }, i++ * 1000, propertyName);
    }
    

    Method 5 (ES6)

    Use a block scoped variable:

    let i = 0;
    for (let propertyName in testObject) {
        setTimeout(() => console.log(testObject[propertyName]), i++ * 1000);
    }
    

    Though I would still recommend using Object.keys with forEach in ES6.

    0 讨论(0)
  • 2020-11-21 07:59

    Replace

     setTimeout("postinsql(topicId)", 4000);
    

    with

     setTimeout("postinsql(" + topicId + ")", 4000);
    

    or better still, replace the string expression with an anonymous function

     setTimeout(function () { postinsql(topicId); }, 4000);
    

    EDIT:

    Brownstone's comment is incorrect, this will work as intended, as demonstrated by running this in the Firebug console

    (function() {
      function postinsql(id) {
        console.log(id);
      }
      var topicId = 3
      window.setTimeout("postinsql(" + topicId + ")",4000); // outputs 3 after 4 seconds
    })();
    

    Note that I'm in agreeance with others that you should avoid passing a string to setTimeout as this will call eval() on the string and instead pass a function.

    0 讨论(0)
  • 2020-11-21 08:02

    I know it's old but I wanted to add my (preferred) flavour to this.

    I think a pretty readable way to achieve this is to pass the topicId to a function, which in turn uses the argument to reference the topic ID internally. This value won't change even if topicId in the outside will be changed shortly after.

    var topicId = xmlhttp.responseText;
    var fDelayed = function(tid) {
      return function() {
        postinsql(tid);
      };
    }
    setTimeout(fDelayed(topicId),4000);
    

    or short:

    var topicId = xmlhttp.responseText;
    setTimeout(function(tid) {
      return function() { postinsql(tid); };
    }(topicId), 4000);
    
    0 讨论(0)
提交回复
热议问题