如何使JavaScript执行等待socket.on函数响应?

时间:2018-07-23 22:31:27

标签: javascript jquery node.js websocket socket.io

var data = bar();

function bar() {
  var result;

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

  return result;
}

在这里,结果值为undefined。如何将从socket.on函数获取的值分配给result变量并返回?

这是我收到socket.io消息事件的另一端。

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

尝试以确认的方式返回对象。

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。我试图传递一个简单的对象

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

该对象的结果值正在传递。


tools.execute(message);正在返回此对象。哪个没有通过ackCallback传递。

{uuid:“ 155C75EA-CB23-4172-85EB-3E256A271D8D”,   名称: '',   类型:“ Object3D”,   父级:null,   儿童:    [{uuid:“ D778A19B-F935-4B06-8536-54494BC5F920”,        名称: '',        类型:“网格”,        家长:[通告],        孩子:[],        上:[对象],        位置:[对象],        旋转:[对象],        四元数:[对象],        比例尺:[对象],        矩阵:[对象],        matrixWorld:[Object],        matrixAutoUpdate:是,        matrixWorldNeedsUpdate:否,        图层:[对象],        可见:正确,        castShadow:否,        receiveShadow:否,        frustumCulled:正确,        renderOrder:0,        用户数据: {},        几何:[对象],        材料:[对象],        drawMode:0},      {uuid:“ 9FD1CACF-6A2B-4932-8FA2-B0A4AF618F8D”,        名称: '',        类型:“网格”,        家长:[通告],        孩子:[],        上:[对象],        位置:[对象],        旋转:[对象],        四元数:[对象],        比例尺:[对象],        矩阵:[对象],        matrixWorld:[Object],        matrixAutoUpdate:是,        matrixWorldNeedsUpdate:否,        图层:[对象],        可见:正确,        castShadow:是的,        receiveShadow:否,        frustumCulled:正确,        renderOrder:0,        用户数据: {},        几何:[对象],        材料:[对象],        drawMode:0}],   上:{x:0,y:1,z:0},   位置:{x:0,y:0,z:0},   回转:    {_x:0,      _y:0,      _z:0,      _order:“ XYZ”,      onChangeCallback:[功能:onRotationChange]},   四元数:    {_x:0,      _y:0,      _z:0,      _w:1      onChangeCallback:[函数:onQuaternionChange]},   比例尺:{x:1,y:1,z:1},   矩阵:{元素:Float32Array [1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1]},   matrixWorld:{元素:Float32Array [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},   matrixAutoUpdate:是,   matrixWorldNeedsUpdate:否,   图层:{mask:1},   可见:正确,   castShadow:否,   receiveShadow:否,   frustumCulled:正确,   renderOrder:0,   userData:{}}

1 个答案:

答案 0 :(得分:3)

您不能“使Javascript等待”来发生异步事件。那不是Javascript的工作方式。这是一个事件驱动的体系结构,根本无法正常工作。

此外,当通过某种异步操作获得该值时,无法直接从该函数返回该值。该函数将在值准备好很久之前返回。您可以查看为何如此的详细信息,以及this canonical answer中有关该常规主题的一些选项。

处理异步结果的现代方法是返回一个将在事件发生时解决的承诺,然后调用方可以使用该承诺来获得最终结果。

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

我在这里显示的是一个更高级的版本,该版本也实现了超时,因此,如果在一定时间内未收到响应,则承诺将被拒绝。这是为了避免创建可能永远存在,永远不会得到回应,因此永远无法解决的承诺。


注意:如果要使用socket.io实现某种类型的请求/响应,则可以从第一条消息中获得直接响应,这是使用socket.io响应特定请求的更好方法。您可以看到ack回调选项如何用于socket.emit() here in the doc。要使用它,您需要连接两端的合作。您发送一条消息并指定一个ack回调。然后,消息的接收者提供ack回调将接收的一些ack数据。如果您同时对同一封邮件提出了其他要求,这将是对您邮件的直接响应,不会使您感到困惑。对于socket.io中的请求/响应,这是首选的体系结构。为了向您展示如何实现它的完整代码示例,我们将必须查看代码的另一端接收到socket.io message事件。


好的,既然您已经显示了连接的另一端,则可能应该使用socket.io的ack响应功能:

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

服务器端:

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。您的服务器端代码在此处显示了username参数:

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

没有这样的username参数传递给该事件处理程序,因此我不确定您从何处获得该参数。您的代码并未真正使用该代码,因此我将其删除(因为它仍然无效)。