1

Completely new to socket io here.

I want to emit data running from within a loop that I am running on my server. The loop runs for a while (a few seconds, up to a few minutes) and I want to emit data from my node server, that I then want to visualize on the client side in the browser (with chart js). Basically real time data charting.

The problem that occured is that apparently the emits are only fired after the loop has finished. From my understanding that is, because the emit function is asynchronous (non-blocking). And since the loop itself blocks until it is finished (originally used a while loop), the asynchronous calls are only fired afterwards. Correct?

while (CONDITION) {
  ...
io.emit('data update', DATA);
}

Now I have come up with my own solution, but it feels clonky. And I am wondering if there are better, more by the book ways to do this. What I am doing now is calling the loop step (where in the end I call emit) recursively as the callback to a setTimeout() with duration set to 0.

function loopStep() {
  if (CONDITION) {
    ...
    io.emit('data update', DATA);
    setTimeout(loopStep,0)
  }
}

setTimeout(loopStep,0);

My question is, are there other, better ways?

EDIT:

I was asked to add the full code.

Here is the full code code with the earlier (loop) version (as far as I can remember it, had to quickly reconstruct it here):

let stp = 0;
while (stp < maxLogStep) {
    let new_av_reward;

        var smallStep = 0;
        

        while (smallStep < maxSmallStep) {


            let ran1 = get_random();
            let cum_prob = 0;

            let chosen_action = pref.findIndex((el, i) => {
                let pro = prob(i);
                let result = pro + cum_prob > ran1;
                cum_prob += pro;
                return result;
            })

            let cur_reward = get_reward(chosen_action);
            if (stp*maxSmallStep + smallStep == 0) {
                new_av_reward = cur_reward
            } else {
                new_av_reward = prev_av_reward + (cur_reward - prev_av_reward) / (stp * maxSmallStep + smallStep)
            }
            let cur_prob = prob(chosen_action);

            pref.forEach((element, index, array) => {
                if (chosen_action === index) {
                    array[index] = element + stepSize * (cur_reward - new_av_reward) * (1 - cur_prob)
                } else {
                    array[index] = element - stepSize * (cur_reward - new_av_reward) * (cur_prob)
                }
            });
            prev_av_reward = new_av_reward;
            smallStep++
        }


        io.emit('graph update', {
            learnedProb: [prob(0), prob(1), prob(2)],
            averageReward: new_av_reward,
            step: stp * maxSmallStep + smallStep
        });
        stp++
    };

And here is the recursive function with setTimeout:

function learnStep(stp, prev_av_reward) {
    let new_av_reward;
    if (stp < maxLogStep) {
        var smallStep = 0;
        

        while (smallStep < maxSmallStep) {


            let ran1 = get_random();
            let cum_prob = 0;

            let chosen_action = pref.findIndex((el, i) => {
                let pro = prob(i);
                let result = pro + cum_prob > ran1;
                cum_prob += pro;
                return result;
            })

            let cur_reward = get_reward(chosen_action);
            if (stp*maxSmallStep + smallStep == 0) {
                new_av_reward = cur_reward
            } else {
                new_av_reward = prev_av_reward + (cur_reward - prev_av_reward) / (stp * maxSmallStep + smallStep)
            }
            let cur_prob = prob(chosen_action);

            pref.forEach((element, index, array) => {
                if (chosen_action === index) {
                    array[index] = element + stepSize * (cur_reward - new_av_reward) * (1 - cur_prob)
                } else {
                    array[index] = element - stepSize * (cur_reward - new_av_reward) * (cur_prob)
                }
            });
            prev_av_reward = new_av_reward;
            smallStep++
        }


        io.emit('graph update', {
            learnedProb: [prob(0), prob(1), prob(2)],
            averageReward: new_av_reward,
            step: stp * maxSmallStep + smallStep
        });
        setTimeout(learnStep, 0, stp + 1, new_av_reward);
    };
user3293924
  • 141
  • 6
  • I think you are looking [listening to variable changes in javascript.](https://stackoverflow.com/a/69389401/13149735) – a_normal_coder233 Nov 27 '21 at 12:34
  • You mean by using a getter/setter for the variable as a property of the WINDOWS object, and calling emit() from inside the setter? That would not change anything, I think, since io.emit() will still be asynchronous and will only be processed after loop is done. – user3293924 Nov 27 '21 at 12:44
  • Looking at the source code for `emit` I don't see it being asynchronous. Can you post the complete code for your `while` loop? – tromgy Nov 27 '21 at 13:07
  • I added it. I fear this will be just more confusing though. The relevant bits are in the code snippet at the top I think. Hopefully the while version is correct, since I reconstructed it from memory. – user3293924 Nov 27 '21 at 14:14
  • @tromgy Do you have a link to emit's source code for me? – user3293924 Nov 27 '21 at 14:26
  • Is this code running in the browser or node server? – tromgy Nov 27 '21 at 14:31
  • Running on Node server – user3293924 Nov 27 '21 at 14:36
  • Then you can look in **node_modules/socket.io/dist/socket.js** for the `emit` function. – tromgy Nov 27 '21 at 14:38
  • I'd suggest stepping through it in the debugger and see if you get messages emitted (and received on the client) in each loop iteration. – tromgy Nov 27 '21 at 14:44
  • With the loop-version, it does emit the messages, ONLY just after the loop is finished. I haven't debugged it. But the code runs for one or two minutes and the emit messages arrive on the client after the server is finished. With the setTimeout it emits them "synchronously" – user3293924 Nov 27 '21 at 14:48
  • The problem also exists in socket-io-cpp. Have you found any solution for Javascript ? – Dimitrios Ververidis Feb 28 '23 at 14:21

1 Answers1

0

If your goal is to make sure you hold up your while loop until the emit completes and the client receives and acknowledges that data, you may want to consider using Promises and socket.io acknowledgement feature like the below example:

Server Side

io.on("connection", async (socket) => {

  // ...
  while (CONDITION) {
    //  ...
    await new Promise((resolve, reject) => {
      io.emit('data update', data, (ack) => {
        //client acknowledged received
        resolve(true)
      });        
    });
  }
  
});

Client Side

socket.on("data update", (data, fn) => {
  console.log(data)
  fn("ack");
});
ucipass
  • 923
  • 1
  • 8
  • 21