1

I have written a small function to animate some multi line strings. It takes an array of strings that it will log. It uses set interval to run the function that will log the frames. Then when their are no frames left clear interval runs and a callback function is called. In my case the callback function is using the readlineSync module to wait for user input (.i.e user can select to continue or exit). Then the script waits for user input. Selecting continue will call the next function, selecting exit will exit.

Basically it runs as intended if after the animation begins the user presses no buttons. But if the user presses the 1 key (the continue key) during the animation, then once the animation is finished the readlineSync prompt flashes briefly and uses that input and continues (same goes for the 2 key and exit).

What I don't understand is how it is using input from before it was actually called? I know this is not a super conventional use case for node, but I would really like to understand what is happening and if there is a way to make this run as desired.

readlineSync has worked for me throughout the project, and I have also used the setInterval loop for other things such as game logic without any issues. It seems to only be an issue when setInterval and readlineSync are used in tandem.

Here's the link to the module: https://www.npmjs.com/package/readline-sync

Here is my simplified code. Thanks in advance for any help towards understanding!

Const readlineSync = require("readline-sync")


let myArray = ["testing", "this", "function"];

let animate = function(array, fps, callback){
    let numberOfFrames = array.length;
    let index = 0;
    let animationLoop = function(){
        console.clear();
        console.log(array[index]);
        index++;
        numberOfFrames--;
        if(numberOfFrames  === 0){
            clearInterval(animationLoopInterval);
            callback();
        }        
    }
    animationLoopInterval = setInterval(animationLoop, 1000/fps);
};


let next = function(){
    let answer = readlineSync.keyInSelect(["Continue", "Exit"]);
    if(answer === 0){
        console.log("Unfortunately this runs from input that came during the animation")
    }else if(answer === 1){
        process.exit();
    }
};

animate(myArray, 1, next);

Brenden
  • 60
  • 7
  • You need to share the readlineSync module. It could be the case that that object is instantiated and listening for stdin before the interval is started. If you are using the native Node Readline in v18 you could try pausing the interface in your animate function and resume it in your next function. – jeeves Jul 26 '22 at 16:48
  • Hi thanks, good idea, I have added the link. I have used the native readline interface before, but unfortunately in this project I wasn't able to make it do what I wanted, though not for lack of trying! Luckily This readlineSync has worked perfectly up until now though. – Brenden Jul 26 '22 at 17:05

1 Answers1

0

That's the natural behavior of the CLI, if user inputs to the terminal while your application is not receiving the input, the data gets buffered and all data is sent to your application the moment you start reading inputs (when you call readlineSync.keyInSelect)

One option to solve this is to make your app to always listen for user data even when you're not expecting any input.

Putting this single line before the animate function should get you your desired behavior:

process.stdin.on("data", ()=>{})
Positivity
  • 5,406
  • 6
  • 41
  • 61
  • 1
    Wow that's really interesting, I absolutely did not know that it was being buffered but was beginning to suspect that. It's always fun to learn those small details that might not make a difference usually...until they do. Thanks a lot for the input. I ll give the fix a try soon when I get back home. – Brenden Jul 26 '22 at 17:00
  • @Brenden yeah, knowing system internals may not seem useful but they come in handy in many scenarios! BTW I ran your code snippet with the fix and it runs as expected, let me know if it didn't work. – Positivity Jul 27 '22 at 12:23
  • Completely agree! Oddly enough the fix works perfectly for the snippet, but when I implement it in my larger script, it does not. I am dealing with input a lot, must be something conflicting somewhere. And so the search begins. – Brenden Jul 27 '22 at 16:13
  • Update. I did get it to work. Following your logic I tried using process.stdin.resume() instead of adding the listener function. Works like a charm. Still not sure why the event trick worked on the snippet but not on the larger script. I even tried disabling all my various listeners and stdin tweaks...very curious. In any case wouldn't have figured this one out on my own. Thanks again for the help! – Brenden Jul 27 '22 at 16:24