2

Question

How can I get NodeJS to only continue the event loop until all arguments have been parsed?

Info

Based on this and this answer, I got Promise for work, so when --password is used in the app I am working on. Now it waits for the users input and doesn't continue the NodeJS event loop right away.

if (argv.password || argv.w) {
  const p = new Promise((res) => {
    read({ prompt: 'Password: ', silent: true }, function(er, password) {
      console.log('Your password is: %s', password);
      res(password);
    })
  });
  p.then((pw) => console.log(pw));
}

The above works perfectly, but when I add

if (argv.update || argv.u) {
  console.log('Not suppose to see this yet')
  cUpdate(argv.input, config, proj, argv.username, password)
}

then also executes this code, which is not what I want.

If I inline the above

if (argv.password || argv.w) {
  const p = new Promise((res) => {
    read({ prompt: 'Password: ', silent: true }, function(er, password) {

      // NEW CODE ADDED HERE
      if (argv.update || argv.u) {
        cUpdate(argv.input, config, proj, argv.username, password)
      }

      res(password);
    })
  });
  p.then((pw) => console.log(pw));
}

then I get this error from cUpdate()

(node:9005) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:9005) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Sandra Schlichting
  • 25,050
  • 33
  • 110
  • 162

2 Answers2

1

You need to handle you errors:

Implement a reject handler if an error occurs.

if (argv.password || argv.w) {
      const p = new Promise((res, rej) => {
        read({ prompt: 'Password: ', silent: true }, function(er, password) {

          // NEW CODE ADDED HERE
          if (er) rej(er)
          if (argv.update || argv.u) {
            cUpdate(argv.input, config, proj, argv.username, password)
          }

          res(password);
        })
      });
      p.then((pw) => console.log(pw)).catch(err=> console.log(err))
    }
EugenSunic
  • 13,162
  • 13
  • 64
  • 86
1

The reason for this is that there's an error in the handler somewhere and I would even suspect that the promise is not in your code but rather in that read method you call.

In order to avoid that, keep your promise handler code as simple as possible and use a chain of then's as much as possible:

if (argv.password || argv.w) {
  const p = new Promise((res) => {
    read({ prompt: 'Password: ', silent: true }, function(er, password) {
      res(password);
    })
  });
  p.then((password) => {
    console.log(password); 
    if (argv.update || argv.u)
      cUpdate(argv.input, config, proj, argv.username, password);
  });
}

Now I would assume that the cUpdate method fails (doesn't look like it's the condition), so we need some error handling - first on the actual prompt and also around the cUpdate.

if (argv.password || argv.w) {
  const p = new Promise((res, rej) => {
    read({ prompt: 'Password: ', silent: true }, function(er, password) {
      if (er) rej(er);
      else res(password);
    })
  });

  p.then((password) => {
    console.log(password); 
    if (argv.update || argv.u)
      cUpdate(argv.input, config, proj, argv.username, password);
  })
  .catch(e => console.error(e));
}

Now you'll probably see the error there if that comes from for instance a missing config or proj entries.

I would also assume that the error may come from inside of the cUpdate method and that it could be asynchronous as well, but since you're not trying to catch the error anywhere and you do not handle the promise it shows the warning there. Luckily the then method allows you to return another Promise and it will also be handled. I'd also simplify the read handler using promisify like this:

const _read = require("util").promisify(read);

if (argv.password || argv.w) {
  _read({ prompt: 'Password: ', silent: true }) // this will essentially handle the error and resolve with the password
    .then(password => {
      console.log(password); 
      if (argv.update || argv.u)
        // all you need it to return the output of the method here if it's a promise.
        return cUpdate(argv.input, config, proj, argv.username, password);
    })
    .catch(e => {
      console.error(e);
      process.exit(10); // this will allow you to see an error in the command line
    });
}
Michał Karpacki
  • 2,588
  • 21
  • 34
  • Which of the last two code snippets would you say is best practice to use? – Sandra Schlichting Apr 28 '20 at 19:44
  • @SandraSchlichting definitely the last one with `promisify`. You can also work it around a bit so that the `if` in that `.then` callback is outside (so you do `then` with a `cUpdate` only if `argv.update` is set. – Michał Karpacki Apr 29 '20 at 08:34