javascript - Hur kan jag skicka en parameter till en setTimeout()-callback?


Jag har lite JavaScript-kod som ser ut så här:

function statechangedPostQuestion()
{
  //alert("statechangedPostQuestion");
  if (xmlhttp.readyState==4)
  {
    var topicId = xmlhttp.responseText;
    setTimeout("postinsql(topicId)",4000);
  }
}

function postinsql(topicId)
{
  //alert(topicId);
}

Jag får ett felmeddelande att topicId inte är definierad. Allt fungerade tidigare innan jag använde setTimeout()-funktionen.

Jag vill att min postinsql(topicId)-funktion ska anropas efter en viss tid. Vad ska jag göra?

Svar #1

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

Du behöver skicka en anonym funktion som en parameter istället för en sträng. Den sistnämnda metoden borde inte ens fungera - enligt ECMAScript-specifikationen - men webbläsare är bara toleranta. Detta är den korrekta lösningen. Lita aldrig på att skicka en sträng som en 'funktion' när du använder setTimeout() eller setInterval(). Det är långsammare eftersom det måste utvärderas och det är helt enkelt inte rätt.

UPPDATERING :

Som Hobblin sa i sina kommentarer till frågan, kan du nu skicka argument till funktionen inuti setTimeout med Function.prototype.bind().

Exempel:

setTimeout(postinsql.bind(null, topicId), 4000);

Svar #2

I moderna webbläsare (dvs IE11 och senare), tar "setTimeout" emot en tredje parameter som skickas som parameter till den interna funktionen i slutet av timern.

Exempel :

var hello = "Hello World";
setTimeout(alert, 1000, hello);

Fler detaljer om den globala funktionen setTimeout() - MDN

Svar #3

Efter att ha gjort lite forskning och testning är den enda korrekta implementeringen:

setTimeout(yourFunctionReference, 4000, param1, param2, paramN);

setTimeout kommer att skicka alla extra parametrar till din funktion så att de kan behandlas där.

Den anonyma funktionen kan fungera för väldigt grundläggande saker, men inom ett objekt där du måste använda "this", finns det inget sätt att få det att fungera. Alla anonyma funktioner kommer att ändra "this" så att det pekar på window, så du kommer att förlora din objektreferens.

Svar #4

Detta är en mycket gammal fråga med ett redan "korrekt" svar, men jag tänkte nämna ett annat tillvägagångssätt som ingen har nämnt här. Detta är kopierat och klistrat från det utmärkta underscore-biblioteket:

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

Du kan skicka så många argument du vill till funktionen som kallas av setTimeout och som en extra bonus (tja, oftast en bonus) är värdet på de argument som skickas till din funktion fryst när du kallar setTimeout, så om de ändrar värde någon gång mellan när setTimeout() kallas och när den går ut, tja... det är inte så fruktansvärt frustrerande längre :)

Här är en fiddle där du kan se vad jag menar.

Svar #5

Jag stötte nyligen på den unika situationen att behöva använda en setTimeout i en loop. Att förstå detta kan hjälpa dig att förstå hur man skickar parametrar till setTimeout.

Metod 1

Använd forEach och Object.keys, enligt Sukimas förslag:

var testObject = {
    prop1: 'test1',
    prop2: 'test2',
    prop3: 'test3'
};

Object.keys(testObject).forEach(function(propertyName, i) {
    setTimeout(function() {
        console.log(testObject[propertyName]);
    }, i * 1000);
});

Jag rekommenderar den här metoden.

Metod 2

Använd 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/

Metod 3

Eller om du inte kan använda forEach eller bind, använd en IIFE:

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

Metod 4

Men om du inte bryr dig om IE < 10, då kan du använda Fabios förslag:

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

Metod 5 (ES6)

Använd en blockscoped variabel:

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

Även om jag fortfarande skulle rekommendera att använda Object.keys med forEach i ES6.

Svar #6

Hobblin har redan kommenterat detta på frågan, men det borde egentligen vara ett svar!

Att använda Function.prototype.bind() är det renaste och mest flexibla sättet att göra detta (med den extra fördelen att kunna sätta this-kontexten):

setTimeout(postinsql.bind(null, topicId), 4000);

För mer information, se dessa MDN-länkar:
https://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041 https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/bind#With_setTimeout

Svar #7

Du kan skicka parametern till setTimeouts återanropsfunktion som:

setTimeout(funktion, millisekunder, param1, param2, ...)

t.ex.

function myFunction() {
  setTimeout(alertMsg, 3000, "Hello");
}

function alertMsg(message) {
    alert(message)
}

Svar #8

Vissa svar är korrekta men invecklade.

Jag svarar på detta igen, 4 år senare, eftersom jag fortfarande stöter på överdrivet komplex kod för att lösa exakt denna fråga. Det FINNS en elegant lösning.

För det första, skicka inte in en sträng som den första parametern när du anropar setTimeout eftersom det effektivt utför ett anrop till den långsamma "eval"-funktionen.

Så hur skickar vi in en parameter till en timeout-funktion? Genom att använda closure:

settopic=function(topicid){
  setTimeout(function(){
    //thanks to closure, topicid is visible here
    postinsql(topicid);
  },4000);
}

...
if (xhr.readyState==4){
  settopic(xhr.responseText);
}

Vissa har föreslagit att använda anonym funktion när man anropar timeout-funktionen:

if (xhr.readyState==4){
  setTimeout(function(){
    settopic(xhr.responseText);
  },4000);
}

Syntaxen fungerar. Men vid den tidpunkt då settopic kallas, dvs. 4 sekunder senare, kan XHR-objektet vara ett annat. Därför är det viktigt att förbindera variablerna i förväg.

Svar #9

Jag vet att det har gått 10 år sedan den här frågan ställdes, men ändå, om du har skrollat hit antar jag att du fortfarande har något problem. Lösningen av Meder Omuraliev är den enklaste och kan hjälpa de flesta av oss, men för dem som inte vill ha några bindningar, här är den:

  1. Använd Param för setTimeout
setTimeout(function(p){
//p == param1
},3000,param1);
  1. Använd omedelbart anropad funktionsuttryck (IIFE)
let param1 = 'demon';
setTimeout(function(p){
    // p == 'demon'
},2000,(function(){
    return param1;
})()
);
  1. Lösning på frågan
function statechangedPostQuestion()
{
  //alert("statechangedPostQuestion");
  if (xmlhttp.readyState==4)
  {
    setTimeout(postinsql,4000,(function(){
        return xmlhttp.responseText;
    })());
  }
}

function postinsql(topicId)
{
  //alert(topicId);
}

Svar #10

Ersätt

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

med

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

eller ännu bättre, ersätt stränguttrycket med en anonym funktion

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

REDIGERA:

Brownstones kommentar är felaktig, detta kommer att fungera som avsett, vilket visas genom att köra detta i Firebug-konsolen

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

Observera att jag håller med andra om att du bör undvika att skicka en sträng till setTimeout eftersom detta kommer att anropa eval() på strängen och istället skicka en funktion.

Svar #11

Mitt svar:

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

Förklaring:

Den anonyma funktionen som skapats returnerar en annan anonym funktion. Denna funktion har tillgång till det ursprungligen skickade topicId, så den kommer inte att göra ett fel. Den första anonyma funktionen kallas omedelbart och skickar in topicId, så den registrerade funktionen med en fördröjning har tillgång till topicId vid tidpunkten för anrop, genom closures.

ELLER

Detta motsvarar i princip:

setTimeout(function() {
  postinsql(topicId); // topicId inside higher scope (passed to returning function)
}, 4000);

REDIGERA: Jag såg samma svar, så titta på hans. Men jag stal inte hans svar! Jag glömde bara att titta. Läs förklaringen och se om det hjälper att förstå koden.

Relaterade artiklar