Search code examples
javascriptnode.jspromisestdinkeystroke

Use keypress to initiate action in node.js


I am using node.js v4.5

I wrote the function below to send repeated messages with a delay.

function send_messages() {
    Promise.resolve()
        .then(() => send_msg() )
        .then(() => Delay(1000) )
        .then(() => send_msg() )
        .then(() => Delay(1000))
        .then(() => send_msg() )        
    ;
}

function Delay(duration) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(), duration);
    });
}

Instead of delay, I would like to activate the sending of messages using a keypress. Something like the function below.

function send_messages_keystroke() {
    Promise.resolve()
        .then(() => send_msg() )
        .then(() => keyPress('ctrl-b') ) //Run subsequent line of code send_msg() if keystroke ctrl-b is pressed
        .then(() => send_msg() )
        .then(() => keyPress('ctrl-b') )
        .then(() => send_msg() )        
    ;
}

Solution

  • You can put process.stdin in raw mode to access individual keystrokes.

    Here's a standalone example:

    function send_msg(msg) {
      console.log('Message:', msg);
    }
    
    // To map the `value` parameter to the required keystroke, see:
    // http://academic.evergreen.edu/projects/biophysics/technotes/program/ascii_ctrl.htm
    function keyPress(value) {
      return new Promise((resolve, reject) => {
        process.stdin.setRawMode(true);
        process.stdin.once('data', keystroke => {
          process.stdin.setRawMode(false);
          if (keystroke[0] === value) return resolve();
          return reject(Error('invalid keystroke'));
        });
      })
    }
    
    Promise.resolve()
      .then(() => send_msg('1'))
      .then(() => keyPress(2))
      .then(() => send_msg('2'))
      .then(() => keyPress(2))
      .then(() => send_msg('done'))
      .catch(e => console.error('Error', e))
    

    It will reject any keystroke that isn't Ctrl-B, but the code is easily modifiable if you don't want that behaviour (and just want to wait for the first Ctrl-B, for instance).

    The value passed to keyPress is the decimal ASCII value of the key: Ctrl-A is 1, Ctrl-B is 2, a is 97, etc.

    EDIT: as suggested by @mh-cbon in the comments, a better solution might be to use the keypress module.