Jejune Jejune - 6 months ago 17
Node.js Question

Do I need closure for every single methods that has a wait-for-result callback?

I was working on a nodejs server with socket.io and MySQL database when I realized that data may change anytime when data retrieval is slow from database.
Let's say for example, on each socket connection, I want to query the database for user's information when they emit X data.

io.on('connection', function(socket) {
console.log('User connected');
socket.on('fetchTable', function(data) {
var number = data.number;
database.getTable(number, function(error, result) {
// We'll take it that result is constant for both: 1
socket.emit('fetchTableResult', result + number);
});
});
});


If 2 users were to connect at the same time (user 1's number: 1, user 2's number: 2), user 1 requested for database BEFORE user 2 but the database was to return the results too slowly for user 1 and processed user 2's data first. Will the data returned be different? Both users will get '3' as the 'fetchTableResult' data right? And won't the socket now be both user 2's socket?

Solution?

io.on('connection', function(socket) {
console.log('User connected');
socket.on('fetchTable', function(data) {
var number = data.number;
(function(socket, number) {
database.getTable(number, function(error, result) {
// We'll take it that result is constant for both: 1
socket.emit('fetchTableResult', result + number);
});
})(socket, number)
});
});


If that's the case, what if inside
database.getTable
, I have another method like fetching a website's HTML content that is going to take some time, do I exactly need another closure inside?

For example:

io.on('connection', function(socket) {
console.log('User connected');
socket.on('fetchTable', function(data) {
var number = data.number;
(function(socket, number) {
database.getTable(number, function(error, result) {
// We'll take it that result is constant for both: 1
(function(socket, result) {
// fetch website html data
socket.emit('fetchTableResult', result + number);
})(socket, result)
});
})(socket, number)
});
});


Reference: https://github.com/loverajoel/jstips/blob/gh-pages/_posts/en/2016-02-03-implementing-asynchronous-loops.md

Answer

I think your first example should work fine. See this for example:

document.addEventListener("click", function(event) {
  var x = event.x;
  setTimeout(function() {
    console.log(x);
  }, 1000);
});

Try clicking in two different places in a short time. As expected, you will get different results.


You need a closure (actually an IIFE) when you're executing an asynchronous function in a loop, for example, this won't work as expected:

for (var i = 0; i < 2; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000*i);
}

You have to either wrap it IIFE, like this:

for (var i = 0; i < 2; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i);
    }, 1000*i);
  }(i));
}

or bind i to the callback, like this:

for (var i = 0; i < 2; i++) {
  setTimeout(function(i) {
    console.log(i);
  }.bind(null, i), 1000*i);
}

Personally I like the second approach more.

However, when you are calling an asynchronous function many times, you don't need an IIFE, because this function is a closure itself. For example:

function test(i) {
  setTimeout(function() {
    console.log(i);
  }, 1000*i);
}

test(0);
test(1);

Every time test() is called, another scope is created, so the i variable isn't overwritten and it works as expected.

Comments