область обратного вызова во время итерации

#javascript #callback

#javascript #обратный вызов

Вопрос:

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

У меня есть цикл each, который вызывает функцию, которая выполняет несколько асинхронных действий, а затем выдает обратный вызов. Я теряю область (очевидно) при срабатывании обратного вызова. Что я сделал, так это передал элемент в цикле функции, а затем вернул его в обратном вызове, чтобы у меня была область, которая мне нужна. Это лучший способ сделать это? Я не ищу чего-то слишком сложного. Я просто хочу убедиться, что я не столкнусь с какими-либо «ошибками».

 function doSomething(varA, varB, self, callback) {
  // do a lot of ajax stuff
  callback(varA   varB, self);
}

$.each( $('.selected'), function(i, item) {
  doSomething('a', 'b', item, function(retVal, self) {
    // process retVal and self
  }
}
 

Ответ №1:

То, что у вас есть, выглядит нормально, но с одной стороны: использование $.each() вместо .each() . Попробуйте это:

 $('.selected').each(function(i, item) {
  doSomething('a', 'b', item, function(retVal, self) {
    // process retVal and self
  }
}
 

Комментарии:

1. Это лучший синтаксис, чем тот, что у меня был.

Ответ №2:

Если вам не нужна ссылка на элемент внутри doSomething , вы можете создать замыкание таким, немного более аккуратным способом:

 function doSomething(varA, varB, callback) {
  // do a lot of ajax stuff
  callback(varA   varB);
}

$.each( $('.selected'), function() {
  var self = this;
  doSomething('a', 'b', function(retVal) {
    // process retVal and self
  }
});
 

Комментарии:

1. На самом деле я пытался использовать замыкание именно таким образом. Если self находился внутри цикла, то он терял область видимости. Если я переместил его за пределы цикла, то цикл выполнялся несколько раз (и менял значение self ), прежде чем когда-либо произойдет обратный вызов. Проблема в том, что цикл продолжает работать, и обратные вызовы срабатывают всякий раз.

Ответ №3:

Основная «ошибка», с которой люди сталкиваются с ajax и циклами, заключается в попытке повторно использовать переменную итерации внутри функции, которая выполняется позже. Например

 for (var i in col) {
  $.ajax({
    url: '...'   i   '.html',
    success: function() {
      console.log(i);  // Why is i different???
    }
  });
}
 

Эта «ошибка» возникает из-за того, что существует 1 экземпляр i shared среди всех success обратных вызовов.

Однако в вашем сценарии у вас есть один i для каждого уровня «итерации». Так что этот гоча тебя не ударит.

Ответ №4:

Поскольку callback определено внутри .each() функции, item все еще находится в области видимости к тому времени, когда вы перейдете к своей функции обратного вызова. Итак, если doSomething() он никогда не используется self , вам не нужно его передавать. Вы можете просто ссылаться item :

 function doSomething(varA, varB, callback) {  
  // do a lot of ajax stuff  
  callback(varA   varB);  
}  

$('.selected').each(function(i, item) {  
  doSomething('a', 'b', function(retVal) {  
    // process retVal and item
  });
});
 

Теперь, если обратный вызов был определен вне .each() , вам нужно было бы сделать это так, как у вас есть, перейдя item к doSomething() :

 function doSomething(varA, varB, self, callback) {  
  // do a lot of ajax stuff  
  callback(varA   varB, self);  
}  
function doSomethingCallback(retVal, self) {  
  // process retVal and item
}

$('.selected').each(function(i, item) {  
  doSomething('a', 'b', item, doSomethingCallback);
});
 

Комментарии:

1. Это имеет смысл для меня, и это то, во что я верил, но так не получилось. И i, и item не определены при запуске обратного вызова.

2. @Jay — В таком случае с вашим кодом происходит нечто большее. Посмотрите эту рабочую демонстрацию: jsfiddle.net/gilly3/czWYK .

3. Я это вижу. Каждый мой блок содержится в функции внутри объекта, а функция, выполняющая обратный вызов, в настоящее время является обычной функцией. Может ли это быть проблемой? Мой фактический код не будет иметь смысла из-за XSS и просто большого количества другого шума, но если я смогу извлечь значимый пример, я это сделаю. Я заставляю его работать, по существу передавая состояние, но это кажется уродливым.

4. @Jay — Важно то, где определяется ваш обратный вызов по отношению к .each() . Если он определен за пределами .each() , вам нужно будет передать элемент в качестве аргументов, как вы делаете в данный момент. Но если обратный вызов — это просто анонимная функция, определенная внутри .each() , эта анонимная функция должна иметь доступ к .each() переменным.