Search code examples
node.jsasynchronoususer-inputblocking

How can I block until user-input received?


I am trying to implement the following script:

A function tries to execute an asynchronous call, and if an exception is thrown then the user is prompt to input whether or not the function should run again.

If the user inputs "y", then the procedure should repeat.

If the user inputs "n", then the procedure should terminate.

If neither, then the question should repeat.

The execution of my entire script should block until either "y" or "n" are input by the user.

Here is what I have so far (with the help of this answer):

async function sendSignedTransaction(rawTransaction) {
    try {
        return await web3.eth.sendSignedTransaction(rawTransaction);
    }
    catch (error) {
        process.stdout.write(error.message + "; try again (y/n)?");
        process.stdin.on("data", async function(data) {
            switch (data.toString().trim()) {
                case "y": return await sendSignedTransaction(rawTransaction);
                case "n": process.exit();
                default : process.stdout.write("try again (y/n)?");
            }
        });            
    }
}

The problem is that the execution of the script continues without waiting until the user has inputted either "y" or "n".


Solution

  • That's because process.stdin operations are also asynchronous so wherever you invoke the initial sendSignedTransaction, if it throws an error there is nothing (currently) in that block of code that stops the function from exiting.

    You have a mixture of Promise & classic callback code. If you want to make sure the caller waits until the function has completely finished, then you can convert the full function into a Promise which gives you more control over it e.g.

    function sendSignedTransaction(rawTransaction) {
      return new Promise(async (resolve, reject) => {
        try {
          const result = await web3.eth.sendSignedTransaction(rawTransaction);
          return resolve(result);
        } catch (e) {
          process.stdout.write(e.message + "; try again (y/n)?");
          process.stdin.on('data', async data => {
            switch (data.toString().trim()) {
              case "y": return resolve(await sendSignedTransaction(rawTransaction));
              case "n": return process.exit();
              default : return process.stdout.write("try again (y/n)?");
            }
          });
        }
      });
    }