【问题标题】:How to make JavaScript execution wait for socket.on function response?如何让 JavaScript 执行等待 socket.on 函数响应?
【发布时间】:2018-12-31 10:48:40
【问题描述】:
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);    
    });  
});

附:我试图传递一个简单的对象

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

正在为此对象传递结果值。


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

{ uuid: '155C75EA-CB23-4172-85EB-3E256A271D8D', 姓名: '', 类型:'Object3D', 父母:空, 孩子们: [ { uuid:'D778A19B-F935-4B06-8536-54494BC5F920', 姓名: '', 类型:“网格”, 父母:[循环], 孩子们: [], 上:[对象], 位置:[对象], 旋转:[对象], 四元数:[对象], 规模:[对象], 矩阵:[对象], 矩阵世界:[对象], 矩阵自动更新:真, 矩阵世界需要更新:假, 层:[对象], 可见:真实, castShadow:假, 接收阴影:假, frustumCulled:是的, 渲染顺序:0, 用户数据: {}, 几何:[对象], 材料:[对象], 绘制模式:0 }, { uuid: '9FD1CACF-6A2B-4932-8FA2-B0A4AF618F8D', 姓名: '', 类型:“网格”, 父母:[循环], 孩子们: [], 上:[对象], 位置:[对象], 旋转:[对象], 四元数:[对象], 规模:[对象], 矩阵:[对象], 矩阵世界:[对象], 矩阵自动更新:真, 矩阵世界需要更新:假, 层:[对象], 可见:真实, castShadow:真的, 接收阴影:假, frustumCulled:是的, 渲染顺序:0, 用户数据: {}, 几何:[对象], 材料:[对象], 绘制模式: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, 0, 1, 0, 0, 0, 0, 1 ] }, 矩阵世界:{ 元素:Float32Array [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] }, 矩阵自动更新:真, 矩阵世界需要更新:假, 层:{掩码:1}, 可见:真实, castShadow:假, 接收阴影:假, frustumCulled:是的, 渲染顺序:0, 用户数据:{} }

【问题讨论】:

  • @SamiX:感谢您的回复,我是 JS 新手。你能用回调修改上面的代码吗?这可以帮助我轻松理解。
  • 你确定tools.execute(message) 是同步的并返回结果吗?
  • @jfriend00:是的,它是同步的。现在我删除了该函数并为其分配了一个虚拟字符串值并进行了测试。不过,我没有得到预期的输出。
  • @Barmar - 我没有将此标记为那个规范问题的重复,因为这只会让 OP 想知道如何使这些技术与他们的 socket.io 东西一起工作,如果你看看这两个我写的并且他们接受的解决方案,他们不可能仅仅通过阅读那个副本来获得这两个选项。所以,我在那里画线。如果您要添加与 OP 的特定代码和他们需要的特定类型的解决方案相关的真正独特的信息,那么仅仅标记该规范答案的另一个副本是没有任何好处的。

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


【解决方案1】:

您不能“让 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
});

我在这里展示的是一个更高级的版本,它也实现了超时,所以如果在一段时间内没有收到响应,promise 将被拒绝。这是为了尽量避免创建一个可能永远挂起、永远不会得到响应从而永远不会解决的承诺。


注意:如果您使用 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);    
    });  
});

附:您的服务器端代码在此处显示了 username 参数:

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

没有这样的username 参数传递给该事件处理程序,所以我不确定你从哪里得到的。您的代码没有显示实际使用它,所以我只是将其删除(因为它无论如何都是无效的)。

【讨论】:

  • 非常感谢您的解释。我已经用接收 socket.io 消息事件的另一端更新了我的问题。
  • @Hariharan - 现在您已经显示了连接的两端,我已经在我的回答中添加了推荐的ack 响应方法。我还添加了良好的日志记录,因此您应该能够看到日志中发生的情况。
  • 非常感谢@jfriend00。我对传递 bar() 函数的参数有疑问它已经超时了。如果我想传递一个参数 bar('dummy value'); .怎么能通过?
  • 非常感谢@jfriend00。我通过了 bar('dummy value', timeout = 10000)。效果很好。
  • @jfriend00 有没有办法在通过不同路径移动时等待套接字连接建立?
猜你喜欢
  • 2014-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-02
  • 1970-01-01
  • 2011-02-19
相关资源
最近更新 更多