Как передать параметр в callbackTimeout()? - программирование

Как передать параметр в callbackTimeout()?

У меня есть код JavaScript, который выглядит так:

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

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

Я получаю сообщение об ошибке topicId Все работало до того, как я использовал функцию setTimeout().

Я хочу, чтобы моя функция postinsql(topicId) вызывалась через некоторое время. Что я должен делать?

4b9b3361

Ответ 1

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

Вам нужно подавать анонимную функцию как параметр вместо строки, последний метод не должен работать даже по спецификации ECMAScript, но браузеры просто снисходительны. Это правильное решение, никогда не полагайтесь на передачу строки как "функции" при использовании setTimeout() или setInterval(), она медленнее, потому что она должна быть оценена, и это просто неправильно.

UPDATE:

Как сказал Хоблин в своих комментариях к вопросу, теперь вы можете передать аргументы функции внутри setTimeout, используя Function.prototype.bind()

Пример:

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

Ответ 2

В современных браузерах "setTimeout" получает третий параметр, который отправляется как параметр во внутреннюю функцию в конце таймера.

Пример:

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

Ответ 3

После проведения некоторых исследований и тестирования единственная правильная реализация:

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

setTimeout передаст все дополнительные параметры вашей функции, чтобы их можно было обработать там.

Анонимная функция может работать для очень простых вещей, но внутри экземпляра объекта, где вы должны использовать "this", нет способа заставить его работать. Любая анонимная функция изменит "this", чтобы указать на окно, поэтому вы потеряете ссылку на объект.

Ответ 4

Это очень старый вопрос с уже "правильным" ответом, но я подумал, что упомяну другой подход, который никто здесь не упомянул. Это скопировано и вставлено из превосходной библиотеки подчеркивания:

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

Вы можете передать столько аргументов, сколько захотите, в функцию, вызываемую setTimeout, и в качестве дополнительного бонуса (обычно это бонус) значение аргументов, передаваемых вашей функции, замораживается при вызове setTimeout, поэтому, если они меняют значение в какой-то момент между вызовом setTimeout() и истечением времени ожидания, ну... это уже не так ужасно расстраивает :)

Здесь скрипка, где вы можете увидеть, что я имею в виду.

Ответ 5

Недавно я столкнулся с уникальной ситуацией, когда вам нужно использовать setTimeout в цикле. Понимание этого может помочь вам понять, как передавать параметры в setTimeout.

Способ 1

Используйте forEach и Object.keys, согласно Sukima предложению:

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

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

Я рекомендую этот метод.

Способ 2

Использовать 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/

Способ 3

Если вы не можете использовать forEach или bind, используйте IIFE:

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

Способ 4

Но если вас не волнует IE <10, то вы можете использовать предложение Фабио:

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

Способ 5 (ES6)

Используйте переменную области с областью:

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

Хотя я бы по-прежнему рекомендовал использовать Object.keys с forEach в ES6.

Ответ 6

Хобблин уже прокомментировал это по этому вопросу, но это должен быть ответ на самом деле!

Использование Function.prototype.bind() - самый чистый и самый гибкий способ сделать это (с добавленным бонусом возможности установить контекст this):

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

Для получения дополнительной информации см. эти ссылки MDN:
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

Ответ 7

Некоторые ответы правильны, но сложны.

Я снова отвечаю на это, 4 года спустя, потому что я все еще сталкиваюсь с чрезмерно сложным кодом, чтобы решить именно этот вопрос. Это элегантное решение.

Прежде всего, не передавайте строку в качестве первого параметра при вызове setTimeout, потому что он эффективно вызывает вызов медленной функции eval.

Итак, как мы передаем параметр функции таймаута? Используя закрытие:

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

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

Некоторые предложили использовать анонимную функцию при вызове функции тайм-аута:

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

Синтаксис работает. Но к тому времени, когда вызывается оконечное устройство, т.е. Через 4 секунды, объект XHR может быть не таким. Поэтому важно предварительно привязать переменные.

Ответ 8

Мой ответ:

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

Пояснение:

Созданная анонимная функция возвращает другую анонимную функцию. Эта функция имеет доступ к изначально прошедшей topicId, поэтому она не сделает ошибку. Первая анонимная функция немедленно вызывается, проходя через topicId, поэтому зарегистрированная функция с задержкой имеет доступ к topicId во время вызова через закрытие.

ИЛИ

Это в основном преобразуется в:

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

EDIT: Я видел тот же ответ, поэтому посмотри на него. Но я не украл его ответ! Я просто забыл посмотреть. Прочтите объяснение и посмотрите, помогает ли он понять код.

Ответ 9

Заменить

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

с

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

или еще лучше, замените строковое выражение на анонимную функцию

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

EDIT:

Комментарий Brownstone неверен, это будет работать по назначению, о чем свидетельствует его запуск в консоли Firebug

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

Обратите внимание, что я согласен с другими, что вам следует избегать передачи строки в setTimeout, поскольку это вызовет eval() в строке и вместо этого передаст функцию.

Ответ 10

Простейшее кросс-браузерное решение для поддержки параметров в setTimeout:

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

Если вы не против поддержки IE 9 и ниже:

setTimeout(postinsql, 4000, topicId);

setTimeout desktop browser compatibility

setTimeout mobile browser compatibility

https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout

Ответ 11

Я знаю это, но я хотел добавить свой (предпочтительный) вкус к этому.

Я думаю, что довольно читаемый способ достичь этого - передать topicId функции, которая, в свою очередь, использует аргумент для ссылки на идентификатор темы внутри. Это значение не изменится, даже если topicId снаружи будет изменено вскоре после.

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

или короткий:

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

Ответ 12

Ответ Дэвида Майстера, похоже, позаботится о параметрах, которые могут измениться сразу после вызова setTimeout(), но до вызова анонимной функции. Но это слишком громоздко и не очень очевидно. Я обнаружил элегантный способ сделать почти то же самое, используя IIFE (сразу inviked выражение функции).

В приведенном ниже примере переменная currentList передается в IIFE, которая сохраняет ее при ее закрытии, пока не будет вызвана функция delayed. Даже если переменная currentList изменяется сразу после показанного кода, setInterval() выполнит правильную работу.

Без этого метода IIFE функция setTimeout() определенно будет вызвана для каждого элемента h2 в DOM, но все эти вызовы будут видеть только текстовое значение last h2 элемент.

<script>
  // Wait for the document to load.
  $(document).ready(function() {
  $("h2").each(function (index) {

    currentList = $(this).text();

    (function (param1, param2) {
        setTimeout(function() {
            $("span").text(param1 + ' : ' + param2 );
        }, param1 * 1000);

    })(index, currentList);
  });
</script>

Ответ 13

это работает во всех браузерах (IE - странный)

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

Ответ 14

Обратите внимание, что причина, по которой topicId была "не определена" в сообщении об ошибке, заключается в том, что она существовала как локальная переменная, когда был запущен setTimeout, но не тогда, когда произошел отложенный вызов postinsql. Особенно важно обратить внимание на переменный ресурс, особенно когда вы пытаетесь что-то вроде передачи "this" в качестве ссылки на объект.

Я слышал, что вы можете передать topicId в качестве третьего параметра функции setTimeout. Не так много деталей, но я получил достаточно информации, чтобы заставить его работать, и это успешно работает в Safari. Я не знаю, что они означают о "миллисекундовой ошибке". Посмотрите здесь:

http://www.howtocreate.co.uk/tutorials/javascript/timers

Ответ 15

Как я решил этот этап?

просто так:

setTimeout((function(_deepFunction ,_deepData){
    var _deepResultFunction = function _deepResultFunction(){
          _deepFunction(_deepData);
    };
    return _deepResultFunction;
})(fromOuterFunction, fromOuterData ) , 1000  );

setTimeout ждет ссылки на функцию, поэтому я создал ее в закрытии, которое интерпретирует мои данные и возвращает функцию с хорошим экземпляром моих данных!

Возможно, вы можете улучшить эту часть:

_deepFunction(_deepData);

// change to something like :
_deepFunction.apply(contextFromParams , args); 

Я тестировал его на chrome, firefox и IE, и он отлично работает, я не знаю о производительности, но мне нужно, чтобы он работал.

образец теста:

myDelay_function = function(fn , params , ctxt , _time){
setTimeout((function(_deepFunction ,_deepData, _deepCtxt){
            var _deepResultFunction = function _deepResultFunction(){
                //_deepFunction(_deepData);
                _deepFunction.call(  _deepCtxt , _deepData);
            };
        return _deepResultFunction;
    })(fn , params , ctxt)
, _time) 
};

// the function to be used :
myFunc = function(param){ console.log(param + this.name) }
// note that we call this.name

// a context object :
myObjet = {
    id : "myId" , 
    name : "myName"
}

// setting a parmeter
myParamter = "I am the outer parameter : ";

//and now let make the call :
myDelay_function(myFunc , myParamter  , myObjet , 1000)

// this will produce this result on the console line :
// I am the outer parameter : myName

Возможно, вы можете изменить подпись, чтобы сделать ее более уместной:

myNass_setTimeOut = function (fn , _time , params , ctxt ){
return setTimeout((function(_deepFunction ,_deepData, _deepCtxt){
            var _deepResultFunction = function _deepResultFunction(){
                //_deepFunction(_deepData);
                _deepFunction.apply(  _deepCtxt , _deepData);
            };
        return _deepResultFunction;
    })(fn , params , ctxt)
, _time) 
};

// and try again :
for(var i=0; i<10; i++){
   myNass_setTimeOut(console.log ,1000 , [i] , console)
}

И наконец, чтобы ответить на исходный вопрос:

 myNass_setTimeOut( postinsql, 4000, topicId );

Надеюсь, это поможет!

ps: извините, но английский это не мой родной язык!

Ответ 16

В общем случае, если вам нужно передать функцию в качестве обратного вызова с определенными параметрами, вы можете использовать функции более высокого порядка. Это довольно элегантно с ES6:

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

setTimeout(someFunction(params), 1000);

Или, если someFunction - первый заказ:

setTimeout(() => someFunction(params), 1000); 

Ответ 17

если вы хотите передать переменную как параметр, давайте попробуем это

если требование - функция и переменная как парма, попробуйте это

setTimeout((param1,param2) => { 
     alert(param1 + param2);
     postinsql(topicId);
},2000,'msg1', 'msg2')

если требование только переменные в качестве параметров, попробуйте это

setTimeout((param1,param2) => { alert(param1 + param2) },2000,'msg1', 'msg2')

Ответ 18

Вы можете попробовать по умолчанию функциональность 'apply()' что-то вроде этого, вы можете передать больше аргументов в качестве вашего требования в массиве

function postinsql(topicId)
{
  //alert(topicId);
}
setTimeout(
       postinsql.apply(window,["mytopic"])
,500);

Ответ 19

@Jiri Vetyska благодарит за сообщение, но в вашем примере что-то не так. Мне нужно было передать цель, которая зависла (это) до функции времени ожидания, и я попробовал ваш подход. Протестировано в IE9 - не работает. Я также сделал некоторые исследования, и кажется, что в качестве здесь третий параметр используется для используемого языка script. Нет никаких дополнительных параметров.

Итак, я последовал за ответом @meder и решил проблему с этим кодом:

$('.targetItemClass').hover(ItemHoverIn, ItemHoverOut);

function ItemHoverIn() {
 //some code here
}

function ItemHoverOut() {
    var THIS = this;
    setTimeout(
        function () { ItemHoverOut_timeout(THIS); },
        100
    );
}
function ItemHoverOut_timeout(target) {
    //do something with target which is hovered out
}

Надеюсь, это полезно для кого-то еще.

Ответ 20

Поскольку проблема с третьим оплотронным параметром в IE и использование замыканий не позволяет нам изменять переменные (например, в цикле) и все еще добиваясь желаемого результата, я предлагаю следующее решение.

Мы можем попробовать использовать рекурсию следующим образом:

var i = 0;
var hellos = ["Hello World1!", "Hello World2!", "Hello World3!", "Hello World4!", "Hello World5!"];

if(hellos.length > 0) timeout();

function timeout() {                
    document.write('<p>' + hellos[i] + '<p>');
    i++;
    if (i < hellos.length)
        setTimeout(timeout, 500);
}

Нам нужно убедиться, что ничего не изменит эти переменные, и мы напишем правильное условие рекурсии, чтобы избежать бесконечной рекурсии.

Ответ 21

//Это два очень простых и кратких ответа:

function fun() {
    console.log(this.prop1, this.prop2, this.prop3);
}

let obj = { prop1: 'one', prop2: 'two', prop3: 'three' };

let bound = fun.bind(obj);

setTimeout(bound, 3000);

 // or

function funOut(par1, par2, par3) {

  return function() { 

    console.log(par1, par2, par3);

  }
};

setTimeout(funOut('one', 'two', 'three'), 5000);

Ответ 22

Я думаю, вы хотите:

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