#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()
переменным.