1
var data = bar();

function bar() {
  var result;

  socket.emit('message', 'test');
  socket.on('msg', function(message) {
    result = message;
  });

  return result;
}

Here, the result value is getting undefined. How to assign the value retrieved from the socket.on function to result variable and return?

This is my other end that receives the socket.io message event.

var io = require('socket.io').listen(server);

io.sockets.on('connection', function(socket, username) {
  socket.on('message', function(message) {
    var result = 'test value'
    socket.emit('msg', result);
  });
});

Trying to return a object in acknowlegment.

io.sockets.on('connection', function (socket) {
    socket.on('message', function(message, ackCallback) {
        console.log("server received message", message);
        var result = tools.execute(message);  
        console.log(typeof(result)); // result is returned a object
        ackCallback(result);    
    });  
});

P.S. I tried to pass a simple object

var result = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};
ackCallback(result);

The result value is getting passed for this object.


tools.execute(message); is returning this object. Which is not getting passed through the ackCallback.

{ uuid: '155C75EA-CB23-4172-85EB-3E256A271D8D', name: '', type: 'Object3D', parent: null, children: [ { uuid: 'D778A19B-F935-4B06-8536-54494BC5F920', name: '', type: 'Mesh', parent: [Circular], children: [], up: [Object], position: [Object], rotation: [Object], quaternion: [Object], scale: [Object], matrix: [Object], matrixWorld: [Object], matrixAutoUpdate: true, matrixWorldNeedsUpdate: false, layers: [Object], visible: true, castShadow: false, receiveShadow: false, frustumCulled: true, renderOrder: 0, userData: {}, geometry: [Object], material: [Object], drawMode: 0 }, { uuid: '9FD1CACF-6A2B-4932-8FA2-B0A4AF618F8D', name: '', type: 'Mesh', parent: [Circular], children: [], up: [Object], position: [Object], rotation: [Object], quaternion: [Object], scale: [Object], matrix: [Object], matrixWorld: [Object], matrixAutoUpdate: true, matrixWorldNeedsUpdate: false, layers: [Object], visible: true, castShadow: true, receiveShadow: false, frustumCulled: true, renderOrder: 0, userData: {}, geometry: [Object], material: [Object], drawMode: 0 } ], up: { x: 0, y: 1, z: 0 }, position: { x: 0, y: 0, z: 0 }, rotation: { _x: 0, _y: 0, _z: 0, _order: 'XYZ', onChangeCallback: [Function: onRotationChange] }, quaternion: { _x: 0, _y: 0, _z: 0, _w: 1, onChangeCallback: [Function: onQuaternionChange] }, scale: { x: 1, y: 1, z: 1 }, matrix: { elements: Float32Array [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] }, matrixWorld: { elements: Float32Array [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] }, matrixAutoUpdate: true, matrixWorldNeedsUpdate: false, layers: { mask: 1 }, visible: true, castShadow: false, receiveShadow: false, frustumCulled: true, renderOrder: 0, userData: {} }

9
  • 2
    Use callback. stackoverflow.com/questions/20337832/… Commented Jul 23, 2018 at 22:34
  • @SamiX: Thanks for your reply I am new to JS. Could you kindly modify the above code with callback. Which can help me to understand easily. Commented Jul 23, 2018 at 22:37
  • Are you absolutely sure that tools.execute(message) is synchronous and returns its result? Commented Jul 23, 2018 at 23:13
  • @jfriend00: Yes it is synchronous. Now I removed the function and assigned a dummy string value to it and tested. Still, I am not getting the expected output. Commented Jul 23, 2018 at 23:27
  • @Barmar - I did not mark this a dup of that canonical question because that would only leave the OP wondering how to make those techniques work with their socket.io stuff and, if you look at the two solutions that I wrote and they accepted, there is no way they could have come to both of those options from only reading that dup. So, I draw the line there. If you have genuinely unique info to add related to the OP's specific code and the specific type of solution they need, then it does nobody any good to just mark yet another dup of that canonical answer. Commented Jul 24, 2018 at 2:31

1 Answer 1

10

You can't "make Javascript wait" for an asynchronous event to occur. That is not how Javascript works. It is an event driven architecture that simply doesn't work that way.

Plus, there is no way to directly return a value from a function when that value is obtained through some sort of asynchronous operation. The function will return long before the value is ready. You can see the details of why this is so and some of the options here in this canonical answer about that general topic.

The modern way to handle an asynchronous result is to return a promise that will be resolved when your event occurs and the caller can then use that promise to get the eventual result.

function bar(data, timeout = 10000) {
    return new Promise((resolve, reject) => {
        let timer;

        socket.emit('message', data);

        function responseHandler(message) {
            // resolve promise with the value we got
            resolve(message);
            clearTimeout(timer);
        }

        socket.once('msg', responseHandler); 

        // set timeout so if a response is not received within a 
        // reasonable amount of time, the promise will reject
        timer = setTimeout(() => {
            reject(new Error("timeout waiting for msg"));
            socket.removeListener('msg', responseHandler);
        }, timeout);

    });
}

bar().then(message => {
   // you can use message here and only in here
});

What I've shown here is a little more advanced version that also implements a timeout so if the response is not received in some amount of time, the promise will reject. This is to try to avoid creating a promise that might just hang out forever, never getting a response and thus never resolving.


Note: If you're implementing some sort of request/response using socket.io, you can get a direct response from your first message which is a better way to use socket.io for a response to a particular request. Yo can see how the ack callback option works for socket.emit() here in the doc. To use it, you need cooperation from both ends of the connection. You send a message and specify an ack callback. The receiver of the message then provides some ack data which your ack callback will receive. This will be a direct response to your message with no chance for it to get confused if there are other requests of the same message also being made at the time. For request/response in socket.io this is the preferred architecture. To show you a complete code example of how to implement it, we would have to see the other end of your code that receives the socket.io message event.


OK, now that you've shown the other end of the connection, you should probably use the ack response feature of socket.io:

function bar(timeout = 10000) {
    return new Promise((resolve, reject) => {
        console.log("client sending message 'test'");
        socket.emit('message', 'test', function(response) {
            console.log("client got ack response", response);
            resolve(response);
        });
    });
}

bar().then(message => {
   // you can use message here and only in here
   console.log("response from bar(): ", message);
});

Server side:

var io = require('socket.io').listen(server);

io.sockets.on('connection', function (socket) {
    socket.on('message', function(message, ackCallback) {
        console.log("server received message", message);
        var result = 'test value'
        console.log("server sending back result", result);
        ackCallback(result);    
    });  
});

P.S. Your server-side code showed a username argument here:

io.sockets.on('connection', function(socket, username) {

There is no such username argument passed to that event handler so I'm not sure where you got that from. Your code didn't show actually using that so I just removed it (because it is invalid anyway).

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks a lot for your explanation. I have updated my question with the other end that receives the socket.io message event.
@Hariharan - Now that you've shown both ends of the connection, I've added to my answer the recommended ack response method. I also added good logging so you should be able to see what is happening in the logs.
Thanks a lot @jfriend00. I have a doubt in passing arguments for bar() function It already holds timeout. If i want to pass a argument bar('dummy value'); . How can it can be passed?
Thanks a lot @jfriend00. I passed bar('dummy value', timeout = 10000). It worked perfectly.
@jfriend00 is there a method to wait till socket connection is established when moving through different paths?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.