Vince Lowe
Vince Lowe

Reputation: 3620

node.js callback inside mysql result loop

I am trying to get my head around this non-blocking business.

Can you tell me what i am doing wrong here? I have tried to add a retrieveContactName function which gets more information from the db before writing to socket. I have tried to make it a callback but i get the error at bottom.

case 'getConversations':
    var sip  = parts[1];
    var pass = parts[2].replace(/[\n\r]/g, ''); //strip that carriage return
    var sql = "SELECT DISTINCT(session) FROM im WHERE sip = "+connection.escape(sip)+" AND password = "+connection.escape(pass)+" ORDER BY timestamp DESC";
    connection.query(sql, function(err, results) {
        if (err) winston.warn(err);  
        for (var i=0; i < results.length; i++) { 
            retrieveContactName(results[i].session, sip, pass, function(value) {
                sock.write('Conversations '+results[i].session+' '+value+'\n');
            });
        }
    });
break;

elsewhere

function retrieveContactName(session, user, pass, callback) {
    var sql = "SELECT `im_from` FROM `im` WHERE `session` = "+connection.escape(session)+" AND `im_to` != "+connection.escape(session)+" AND `sip` = "+connection.escape(user)+"  AND `password` = "+connection.escape(pass)+" LIMIT 1";
    connection.query(sql, function(err, results) {
        if (err) winston.warn(err);  
        if (results.length > 0 ) { 
            callback(results[0].im_from);
        } else {
            callback(session.replace("contact:",""));
        }
    });
}

and my error

TypeError: Cannot read property 'session' of undefined
    at /home/ubuntu/socket/server.js:44:47
    at Query._callback (/home/ubuntu/socket/server.js:79:4)
    at Query.end (/home/ubuntu/socket/node_modules/mysql/lib/protocol/sequences/Sequence.js:75:24)
    at Query._handleFinalResultPacket (/home/ubuntu/socket/node_modules/mysql/lib/protocol/sequences/Query.js:143:8)
    at Query.EofPacket (/home/ubuntu/socket/node_modules/mysql/lib/protocol/sequences/Query.js:127:8)
    at Protocol._parsePacket (/home/ubuntu/socket/node_modules/mysql/lib/protocol/Protocol.js:172:24)
    at Parser.write (/home/ubuntu/socket/node_modules/mysql/lib/protocol/Parser.js:62:12)
    at Protocol.write (/home/ubuntu/socket/node_modules/mysql/lib/protocol/Protocol.js:37:16)
    at Socket.ondata (stream.js:38:26)
    at Socket.emit (events.js:88:20)

Upvotes: 1

Views: 3816

Answers (2)

Pebbl
Pebbl

Reputation: 36075

Your problem is down to not fully understanding scope, basically the issue is occurring here:

for (var i=0; i < results.length; i++) { 
    retrieveContactName(results[i].session, sip, pass, function(value) {
        sock.write('Conversations '+results[i].session+' '+value+'\n');
    });
}

The above will not work as you expect, all because by the time your callback fires i will not have the value you expect... it will most likely be equal to results.length which will give you an undefined slot in results. This is because the for will most likely have continued on and finished it's execution before the callbacks ever occur. This is the principal of non-blocking, code does not wait, it carries on, and your callbacks have to be prepared for this.

In order to use the value of i, or any variable that may change it's value outside the scope of your callback, you need to capture that value and store it with your callback. There are a few ways to do this but the nicest way is to pass the data your callback requires in as arguments — so you need to pass the results[i].session (passed into retrieveContactName) on to your callback.

function retrieveContactName(session, user, pass, callback) {
    var sql = "SELECT `im_from` FROM `im` WHERE `session` = "+connection.escape(session)+" AND `im_to` != "+connection.escape(session)+" AND `sip` = "+connection.escape(user)+"  AND `password` = "+connection.escape(pass)+" LIMIT 1";
    connection.query(sql, function(err, results) {
        if (err) winston.warn(err);  
        if (results.length > 0 ) { 
            callback(results[0].im_from, session);
        } else {
            callback(session.replace("contact:",""), session);
        }
    });
}

And then:

for (var i=0; i < results.length; i++) { 
    retrieveContactName(results[i].session, sip, pass, function(value, session) {
        sock.write('Conversations '+session+' '+value+'\n');
    });
}

Obviously this is just a quick example, it may be best to pass in the actual result row — depending on what you want / need.

Upvotes: 2

vitez
vitez

Reputation: 83

results[i] may be out of scope inside the callback

case 'getConversations':
var sip  = parts[1];
var pass = parts[2].replace(/[\n\r]/g, ''); //strip that carriage return
var sql = "SELECT DISTINCT(session) FROM im WHERE sip = "+connection.escape(sip)+" AND password = "+connection.escape(pass)+" ORDER BY timestamp DESC";
connection.query(sql, function(err, results) {
    if (err) winston.warn(err);  
    for (var i=0; i < results.length; i++) { 
        retrieveContactName(results[i].session, sip, pass, function(value) {
            sock.write('Conversations '+results[i].session+' '+value+'\n');
        });
    }
});

break;

can you place a console.log(results) just above:

 sock.write('Conversations '+results[i].session+' '+value+'\n');

Upvotes: 0

Related Questions