3

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".

halfer
  • 19,824
  • 17
  • 99
  • 186
goodvibration
  • 5,980
  • 4
  • 28
  • 61

1 Answers1

1

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)?");
        }
      });
    }
  });
}
James
  • 80,725
  • 18
  • 167
  • 237
  • Shouldn't this be `async function sendSignedTransaction`, as it returns a `Promise` object? – goodvibration Jan 06 '19 at 11:42
  • In addition to that: 1. The "n" part doesn't work (the process does not terminate). 2. Every time I press "y" and the error repeats, I can one more "try again" printout (i.e., 1 printout, then 2 printouts, then 3 printouts, ...). – goodvibration Jan 06 '19 at 11:51
  • OK, sorry, the "n" IS working, and the multi-printing I was able to solve by changing `on` to `once`. Thank you! – goodvibration Jan 06 '19 at 12:14
  • @goodvibration to answer the first question, no it doesn't. Marking a function as `async` simply enables the use of `await` syntax inside the function, it also implicitly returns a Promise. If you explicitly return a Promise, as long as you don't need to use `await` within that function scope (which we don't here) then you don't need `async`. On the second one, I see you've solved this, however I only changed the `y` scenario, the other code regarding the `n` scenario was what you already had. – James Jan 06 '19 at 12:27
  • @goodvibration changing from `on` to `once` is _not_ the correct fix, for example if you enter something other than `y` /`n` then you'll have an unresolved promise and you can't ever fix it. The issue with the repeating message is simply because there is no break between each case statement, when `y` executes, it's actually going to run `n` and then `default` - I've updated the code to fix this. – James Jan 06 '19 at 12:32
  • AFAIK, you don't need a `break`: 1. after `return`. 2. after `exit`. 3. at the end of the `default` case. – goodvibration Jan 06 '19 at 12:56
  • @goodvibration correct, however the issue was in my code example was I didn't `return` on the `y` scenario hence why the message would repeat (the switch was falling through the other case statements) - I've updated to return in all cases just for completeness. – James Jan 06 '19 at 13:29
  • Oh, right. I did have a `return` in my code, but I guess that I have overridden it with your code. I'll give it a try now, thanks. – goodvibration Jan 06 '19 at 13:33
  • BTW, my entire infrastructure is in a class. So far, I've used `this` everywhere needed. Now, inside the `Promise`, the `this` is "unknown" for some reason, so I'm having to pass it to the function from the outside. Any workaround for this? – goodvibration Jan 06 '19 at 13:35
  • Sorry, but the printout-count grows by 1 after every "y". I think that with `process.stdin.on`, you are creating a new listener every time. You can view the answer that I have linked in my question (an answer to a previous question of mine), which actually refers to this problem specifically. – goodvibration Jan 06 '19 at 13:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/186264/discussion-between-james-and-goodvibration). – James Jan 06 '19 at 18:31