0

I'm trying to make a chat app (similar to Omegle.com) that matches native speakers to learners. I believe I have the correct algorithm, but my Javascript keeps throwing a message that Javascript heap out of memory error.

For example, let's say Speaker 1 speaks English and learns French and Speaker 2 speaks French and learns English (perfect match). My algorithm displays match found.

Queue Controller

socket.emit('in queue', {
    speakingLanguages: speakingLanguages,
    learningLanguages: learningLanguages
});

socket.on('chat start', function(data){
    room = data.room;
    $location.path('/chat');
});

server.js

var queue = [];
var rooms = {};

io.sockets.on('connection', function(socket){
    console.log('User ' + socket.id + ' connected');

    socket.on('in queue', function(data){
        socket.speakingLanguages = data.speakingLanguages;
        socket.learningLanguages = data.learningLanguages;
        if (queue.length != 0){
            for (var i = 0; i < queue.length; i++){
                for (var j = 0; j < queue[i].speakingLanguages.length; j++){
                    for (var y = 0; y < socket.learningLanguages.length; y++){
                        if (queue[i].speakingLanguages[j] === socket.learningLanguages[y]){
                            console.log('a match was found!');
                            break;
                        }else{
                            queue.push(socket);
                            break;
                        }
                    }
                }
            }
        }else{
            queue.push(socket);
        }
    });

    socket.on('send message', function(data){
        var room = rooms[socket.id];
        io.sockets.in(room).emit('send message', data);
    });

});

But see, when I open up multiple windows on my browser to test a lot of cases, I get that error above. It says that I ran out of Javascript heap and I've been struggling with this for hours, but I don't know what to do.

Information:

  • speakingLanguages and learningLanguages are of type array (because people can speak multiple languages and learn multiple languages)
  • Every time a match is not found, I put them into the queue array
Student22
  • 1,979
  • 5
  • 22
  • 33

3 Answers3

0

I was diligently writing down notes and investigating the algorithm and I think I've finally got it. I tested the algorithm and it seems to be working!

socket.on('in queue', function(data){
    socket.speakingLanguages = data.speakingLanguages;
    socket.learningLanguages = data.learningLanguages;
    var found = false;
    if (queue.length != 0){
        for (var i = 0; i < socket.learningLanguages.length; i++){
            for (var j = 0; j < queue.length; j++){
                for (var y = 0; y < queue[j].speakingLanguages.length; y++){
                    if (socket.learningLanguages[i] === queue[j].speakingLanguages[y]){
                        console.log('a match was found');
                        var peer = queue.pop();
                        var room = socket.id + '#' + peer.id;

                        peer.join(room);
                        socket.join(room);

                        rooms[peer.id] = room;
                        rooms[socket.id] = room;

                        peer.emit('chat start', { room: room });
                        socket.emit('chat start', { room : room });
                        found = true;
                        break;
                    }
                }
            }
        }
        if (!found){
            queue.push(socket);
        }
    }else{
        queue.push(socket);
    }
});
Student22
  • 1,979
  • 5
  • 22
  • 33
0

There is way too much for loop nesting in your code.
I made some changes to your code.

io.sockets.on('connection', function(socket){
  console.log('User ' + socket.id + ' connected');

  socket.on('in queue', function(data){
    socket.speakingLanguages = data.speakingLanguages;
    socket.learningLanguages = data.learningLanguages;
    var searchingFor = [];
    var toQueue = {};
    var toUnqueue = {};
    for (let spoken of socket.speakingLanguages) {
      for (let learning of socket.learningLanguages) {
        var searchKey = `S:${learning}L:${spoken}`;
        var queueKey = `S:${spoken}L:${learning}`;
        searchingFor.push(searchKey);
        toQueue[queueKey] = socket;
        toUnqueue[queueKey] = undefined;//Using `undefined` instead of `delete` for performance 
      }
    }
    //Search for a peer
    var peer = false;//use an array[] if you want all matches
    for (let searching of searchingFor) {
      let result = queue[searching];
      if(result) {
        peer = result;
        break;//You can change this part to find all matches
      }
    }
    if (!peer) {
      //No peer(s) found
      //Add all possible combination of `spoken:learning` keys to the queue
      //If someone searchs for one of these combinations we will be matched
      socket.toUnqueue = toUnqueue;
      Object.assign(queue, toQueue);
    } else {
      //We found a matching peer
      console.log('a match was found');//If you use multiple matches you can ask what he prefers
      //Unqueue other language combinations of the peer
      Obeject.assign(queue, peer.toUnqueue);

      //The rest of you logic goes here
      var room = socket.id + '#' + peer.id;
      peer.join(room);
      socket.join(room);

      rooms[peer.id] = room;
      rooms[socket.id] = room;

      peer.emit('chat start', { room: room });
      socket.emit('chat start', { room : room });
    }
  });

  socket.on('send message', function(data){
    var room = rooms[socket.id];
    io.sockets.in(room).emit('send message', data);
  });
});

I changed the way you're using the queue. Searching by key should be faster.
I didn't test it but it should be good.
Tell me if it does the job!

amenzou
  • 319
  • 1
  • 7
  • thanks so much for this improved algorithm. give me time to put this in my code and i'll contact you as soon as possible! really appreciate you doing this for me, amenzou – Student22 Aug 08 '17 at 02:06
  • hey may you please clarify what exactly "availableKey" is? i'm assuming it's just "socket.id"? also i'm getting a "peer.emit is not a function" error when i run this. i'll investigate this algorithm more to see if i can come up with anything. thanks for your help! – Student22 Aug 08 '17 at 03:33
  • Sorry I changed the name of `availableKey` for `queueKey` and forgot to change it everywhere. Also there was a typo with the word `learning`. Should work now. I edited the code. Peer is the socket. – amenzou Aug 08 '17 at 11:19
  • @Student22 did you have the time to test the corrections I made? – amenzou Aug 09 '17 at 16:15
  • yes! i've written a new answer here. thanks for your help! – Student22 Aug 11 '17 at 01:12
0

I revised my code a bit using amenzou's answer and another one here

var queue = [];
var rooms = {};
var index = 0;

var intersect_safe = function(a, b){
  var ai=0, bi=0;
  var result = [];

  while( ai < a.length && bi < b.length )
  {
     if      (a[ai] < b[bi] ){ ai++; }
     else if (a[ai] > b[bi] ){ bi++; }
     else /* they're equal */
     {
       result.push(a[ai]);
       ai++;
       bi++;
     }
  }

  return result;
}

io.sockets.on('connection', function(socket){
    console.log('User ' + socket.id + ' connected');

    socket.on('in queue', function(data){
        socket.speakingLanguages = data.speakingLanguages;
        socket.learningLanguages = data.learningLanguages;

        if (queue.length != 0){
            for (var i = 0; i < queue.length; i++){
                var match = false;
                var match1 = [];
                var match2 = [];
                match1 = intersect_safe(socket.learningLanguages, queue[i].speakingLanguages);
                match2 = intersect_safe(socket.speakingLanguages, queue[i].learningLanguages);
                if (match1.length != 0 && match2.length != 0){
                    console.log('match');
                    index = i;
                    match = true;
                    break;
                }
            }

            if (match){
                var peer = queue.splice(index, 1);
                var room = socket.id + '#' + peer[0].id;

                peer[0].join(room);
                socket.join(room);

                rooms[peer[0].id] = room;
                rooms[socket.id] = room;

                peer[0].emit('chat start', match2);
                socket.emit('chat start', match1);
            }else{
                queue.push(socket);
            }
        }else{
            queue.push(socket);
        }
    });

Works perfectly! Thank you very much for your help and advice, amenzou.

Student22
  • 1,979
  • 5
  • 22
  • 33