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: {} }

Hariharan
  • 13
  • 1
  • 1
  • 6
  • 2
    Use callback. https://stackoverflow.com/questions/20337832/is-socket-io-emit-callback-appropriate – Mahmal Sami Jul 23 '18 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. – Hariharan Jul 23 '18 at 22:37
  • Are you absolutely sure that `tools.execute(message)` is synchronous and returns its result? – jfriend00 Jul 23 '18 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. – Hariharan Jul 23 '18 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. – jfriend00 Jul 24 '18 at 02:31
  • @jfriend00: I am trying to pass an object in ackCallback(resultObj); but the acknowledgement is not getting received. It works only for the string variables. Is there any particular way that object variables could be passed through ackCallback? – Hariharan Jul 24 '18 at 23:07
  • @Hariharan - there is no magic way. Show the code you're trying it with in an edit to the end of your question. – jfriend00 Jul 24 '18 at 23:28
  • @jfriend00: I have updated with the object value which I am trying to pass through the socket acknowledgment. – Hariharan Jul 24 '18 at 23:58
  • socket.io passes objects by serializing them to JSON. It may be that your object can't be serialized to JSON because of circular dependencies. You can check that by trying `console.log(JSON.stringify(result));` and see if that throws an exception or not. If there are circular dependencies, then you would have to remove them from the object (objects pointing at other objects which point back to prior objects) before it can be serialized or just copy the properties you need into a new object and send that. – jfriend00 Jul 25 '18 at 00:18

1 Answers1

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

Manas Khandelwal
  • 3,790
  • 2
  • 11
  • 24
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks a lot for your explanation. I have updated my question with the other end that receives the socket.io message event. – Hariharan Jul 23 '18 at 23:12
  • @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. – jfriend00 Jul 23 '18 at 23:35
  • 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? – Hariharan Jul 24 '18 at 00:28
  • Thanks a lot @jfriend00. I passed bar('dummy value', timeout = 10000). It worked perfectly. – Hariharan Jul 24 '18 at 01:04
  • @jfriend00 is there a method to wait till socket connection is established when moving through different paths? – Proo1931 Aug 24 '20 at 18:08
  • @Proo1931 - Are you talking about client-side code? I think it would probably be best if you asked your own question and showed a code example for what problem you're trying to solve. – jfriend00 Aug 24 '20 at 18:10
  • I'm talking about server side code, I have create a question as you suggested - https://stackoverflow.com/questions/63568260/wait-for-socket-connection-to-establish-when-changing-page-path-till-sending thanks for taking a time to look at it. – Proo1931 Aug 24 '20 at 20:25