P
P
Pavel Shvedov2014-10-17 16:46:25
JavaScript
Pavel Shvedov, 2014-10-17 16:46:25

Why does magic happen in JS code?

Kind people, explain, maybe at the end of the working day and week I don’t notice any jamb in my code. I am writing a small application on a node, for convenience I write in CoffeeScript. The application needs to poll some servers via HTTP. Server data is stored as a list of JSON objects. I run through them in a loop and make a request for each and hang up a callback for the answer, in the callback I use the m variable to update the data for myself, regarding this particular monitor.
For requests I use the Request module. The code below, everything seems to be ok, I inserted the debug, as there were errors in the work. I output to the console for which monitors we are making a request, and accordingly I display in the callback when I received the answer to make sure that we took the data from all of them.

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)
            )

But the debug says an absurd thing:

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"}

Requests were made for both servers, and the response was received from one twice. It seems that m containing information about the second server somehow got into the callback for the first server, because the data in the response body is different, but how did this happen?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
N
Nikita Gushchin, 2014-10-17
@mmmaaak

I'm not a CoffeeScript expert, but apparently your script generates the following JS code ( link to 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);
}

In this case, you are closing your anonymous function to the variable el, whose value changes on each iteration. Thus, after the end of the loop, the value of el will be equal to the last element of the array. And since the anonymous function will be called after the loop ends, this will lead to the result that you described:
timeout Object { id: 2 }
timeout Object { id: 2 }
timeout Object { id: 2 }
In order for your code to run correctly, you can do this trick: pass el as an argument to a function that will return a callback function to us ( example on 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);        
}

In this case, when creating a callback, we will always have the current value of el in the scope:
timeout Object { id: 0 }
timeout Object { id: 1 }
timeout Object { id: 2 }

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question