Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

socket.on event gets triggered multiple times

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

i've created a server based chat-bot application using node.js socket.io and express but the thing is for first time when i call socket.on it gets executed once and for 2nd time it gets executed twice for 3rd thrice and so on i've tackled this issue by setting a flag on my client so that it would display only once. i just wants to know is my code logically correct i mean is this a good code? because if the client ask a question for 10th time than listeners array will have 10+9+8....+1 listeners it would go on increasing depending upon number of questions clients asked. which is not good

i tried using removeListener it just removes listener once and it dosent call back for 2nd time. what do you guys recommend? do i go with this or is there any other way to add the listener when socket.on called and remove it when it gets executed and again add listener for the next time it gets called

thank-you.

client code:

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;
  }
 });
}
like image 439
Prem Avatar asked Apr 12 '18 05:04

Prem


1 Answers

The problem is caused by your client code. Each time you call the reply() function in the client you set up an additional socket.on('Ans', ...) event handler which means they accumulate. You can change that to socket.once() and it will remove itself each time after it get the Ans message. You can then also remove your flag variable.

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 is not really built as a request/response system which is what you are trying to use it as. An even better way to implement this would be to use the ack capability that socket.io has so you can get a direct response back to your Quest message you send.

You also need to fix your shared variables replyFromBot and listen on your server because those are concurrency problems waiting to happen as soon as you have multiple users using your server.


Better Solution

A better solution would be to use the ack capability that socket.io has to get a direct response to a message you sent. To do that, you'd change your server to this:

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

And, change your client code to this:

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

Doing it this way, you're hooking into a direct reply from the message so it works as request/response much better than the way you were doing it.

like image 122
jfriend00 Avatar answered Sep 28 '22 03:09

jfriend00