socket.on事件被多次触发

时间:2018-04-12 05:09:08

标签: node.js express socket.io

var express = require('express');
var app = express();
var server = app.listen(3000);
var replyFromBot;
app.use(express.static('public'));
var socket = require('socket.io');
var io = socket(server);
io.sockets.on('connection' , newConnection);

function newConnection(socket) {
   console.log(socket.id);
   listen = true;
   socket.on('Quest' ,reply);
   function reply(data) {
     replyFromBot = bot.reply("local-user", data);
     console.log(socket.id+ " "+replyFromBot);
     socket.emit('Ans' , replyFromBot);
  }
}

我已经使用node.js socket.io和express创建了一个基于服务器的聊天机器人应用程序,但这是我第一次调用socket.on它执行一次,第二次执行两次第三次等等我通过在我的客户端设置一个标志来解决这个问题,这样它只会显示一次。我只是想知道我的代码在逻辑上是否正确我的意思是这是一个很好的代码?因为如果客户问第10次问题,那么听众阵列将会有10 + 9 + 8 ...... + 1个听众,这将继续增加,具体取决于客户提出的问题数量。哪个不好

我尝试使用removeListener它只删除一次侦听器,并且第二次调用它。你们推荐什么?我是这样做的还是有任何其他方法在socket.on调用时添加监听器,并在执行时删除它,并在下次调用时再次添加监听器

感谢你。

客户代码:

function reply() {
  socket.emit('Quest' , Quest);
  flag = true;
  audio.play();
  socket.on('Ans', function(replyFromBot) {
  if(flag) {
    console.log("hi");
    var para = document.createElement("p2");
    x = document.getElementById("MiddleBox");
    para.appendChild(document.createTextNode(replyFromBot));
    x.appendChild(para);
    x.scrollTop = x.scrollHeight;
    flag = false;
  }
 });
}

2 个答案:

答案 0 :(得分:4)

问题是由您的客户端代码引起的。每次在客户端中调用reply()函数时,都会设置一个额外的socket.on('Ans', ...)事件处理程序,这意味着它们会累积。您可以将其更改为socket.once(),并且每次收到Ans消息后它都会自行删除。然后,您还可以删除flag变量。

function reply() {
  socket.emit('Quest' , Quest);
  audio.play();
  // change this to .once()
  socket.once('Ans', function(replyFromBot) {
    console.log("hi");
    var para = document.createElement("p2");
    x = document.getElementById("MiddleBox");
    para.appendChild(document.createTextNode(replyFromBot));
    x.appendChild(para);
    x.scrollTop = x.scrollHeight;
  });
}

Socket.io并不是真正构建为请求/响应系统,而是您尝试将其用作。实现此目的的更好方法是使用ack capability that socket.io has,以便您可以直接回复您发送的Quest邮件。

您还需要修复服务器上的共享变量replyFromBotlisten,因为只要有多个用户使用您的服务器,这些就会出现并发问题。

更好的解决方案

更好的解决方案是使用socket.io具有的ack功能来直接响应您发送的消息。为此,您需要将服务器更改为:

function newConnection(socket) {
    console.log(socket.id);
    socket.on('Quest', function(data, fn) {
      let replyFromBot = bot.reply("local-user", data);
      console.log(socket.id+ " "+replyFromBot);
      // send ack response
      fn(replyFromBot);
    });
}

然后,将您的客户端代码更改为:

function reply() {
    audio.play();
    socket.emit('Quest', Quest, function(replyFromBot) {
        console.log("hi");
        var para = document.createElement("p2");
        x = document.getElementById("MiddleBox");
        para.appendChild(document.createTextNode(replyFromBot));
        x.appendChild(para);
        x.scrollTop = x.scrollHeight;
    });
}

这样做,你正在接收来自消息的直接回复,因此它比请求/响应更好地工作。

答案 1 :(得分:1)

而不是<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> 尝试socket.on('Quest' ,reply);

代码中的错误是每次调用socket.once('Quest' ,reply);节点时都会注册一个事件监听器'Quest'。因此,第一次调用newConnection()时,事件'Quest'的事件监听器的数量为1,第二次调用函数,事件监听器的数量增加到2,依此类推

newConnection()确保绑定到套接字的事件监听器的数量与注册的事件'Quest'恰好是一个