Почему происходит магия в JS коде?

Люди добрые, поясните, может я под конец рабочего дня и недели не замечаю какой-нибудь косяк в своем коде. Пишу небольшое приложение на ноде, для удобства пишу на CoffeeScript. В приложении нужно опрашивать некие сервера по HTTP. Данные о серверах хранятся в виде списка JSON объектов. Пробегаю по ним циклом и делаю для каждого запрос и вешаю колбек на ответ, в колбеке использую переменную m, чтоб обновить данные у себя, относительно этого конкретного монитора.
Для запросов использую модуль Request. Код ниже, вроде все ок, вставил дебаг, так как наблюдались ошибки в работе. Вывожу в консоль для каких мониторов делаем запрос, и соответственно вывожу в колбеке, когда ответ получил, чтоб удостовериться, что ото всех мы данные забрали.
sync: (monitors) ->
        for m in monitors
            console.log "Sending sync request to monitor: #{JSON.stringify(m)}"
            request.post({
                url: "http://#{m.host}:#{m.port}",
                form: {
                    request_id: 2
                }
            }, (err, response, body) =>
                if err?
                    console.log "Monitor sync error: #{err.message}"
                    return 0
                console.log "Response from monitor #{JSON.stringify(m)}"
                data = JSON.parse(body)
                urls = JSON.parse(@crypto_helper.decrypt(data.data))
                @alert_urls(m, urls)
            )

Но дебаг говорит несуразную вещь:

Sending sync request to monitor: {"monitor_id":1,"host":"z.zzz.zzz.zzz","port":YYYY,"name":"Local Monitor"}
Sending sync request to monitor: {"monitor_id":2,"host":"xxx.xxx.xxx.x","port":YYYY,"name":"Remote Monitor"}
Response from monitor {"monitor_id":2,"host":"xxx.xxx.xxx.xxx","port":YYYY,"name":"Remote Monitor"}
Response from monitor {"monitor_id":2,"host":"xxx.xxx.xxx.xxx","port":YYYY,"name":"Remote Monitor"}


Запросы делали для обоих серверов, а ответ получили от одного дважды. Такое ощущение, что в колбек для первого сервера, как-то попал m содержащий информацию о втором сервере, потому, что данные в теле ответа разные, но как так произошло?
  • Вопрос задан
  • 2505 просмотров
Решения вопроса 1
Я не знаток CoffeeScript, но судя по всему, ваш скрипт генерирует вот такой JS код (ссылка на jsfiddle):
var a = [ { id: 0 }, { id: 1 }, { id: 2 } ];
for(var i = 0; i != a.length; ++i)
{
    var el = a[i];
    setTimeout(function() { 
        console.log('timeout',el);
    },100);
}


В этом случае вы замыкаете вашу анонимную функцию на переменную el, значение которой меняется на каждой итерации. Таким образом после окончания цикла значение el будет равно последнему элементу массива. А так как анонимная функция будет вызвана после завершения цикла, то это приведет к результату, который вы описали:
timeout Object { id: 2 }
timeout Object { id: 2 }
timeout Object { id: 2 }


Для того, чтобы ваш код выполнялся правильно - можно сделать такой трюк: передавать el в качестве аргумента в некую функцию, которая возвратит нам коллбэк функцию (пример на jsfiddle):
var a = [ { id: 0 }, { id: 1 }, { id: 2 } ];
var createCB = function(e){
    return function() { 
        console.log('timeout',e);
    };
};
for(var i = 0; i != a.length; ++i)
{
    var el = a[i];
    setTimeout(createCB(el),100);        
}


В этом случае при создании коллбэка у нас в области видимости будет всегда текущее значение el:
timeout Object { id: 0 }
timeout Object { id: 1 }
timeout Object { id: 2 }
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы