18
votes

UnhandledPromiseRejectionWarning on async await promise

I have this code:

function foo() {
  return new Promise((resolve, reject) => {
    db.foo.findOne({}, (err, docs) => {
      if (err || !docs) return reject();
      return resolve();
    });
  });
}

async function foobar() {
  await foo() ? console.log("Have foo") : console.log("Not have foo");
}

foobar();

Which results with:

(node:14843) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): false

(node:14843) [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.

Note: I know I can solve this issue like this:

foo().then(() => {}).catch(() => {});

But then we are "back" to callbacks async style.

How do we solve this issue?

3
What's db.foo? Is it Mongoose?Estus Flask
@estus No, it's MongoJS, see here github.com/mafintosh/mongojsRaz
try/catch is necessary for async/await. If you want to write those codes with async/await style, you can try util.promisify, which deal with functions following the error-first callback style.39ecneret
What do you think is wrong with foo().catch(console.error)?Bergi

3 Answers

21
votes

Wrap your code in try-catch block.

async function foobar() {
  try {
    await foo() ? console.log("Have foo") : console.log("Not have foo");
  }
  catch(e) {
    console.log('Catch an error: ', e)
  }
}
5
votes

then(() => {}).catch(() => {}) isn't needed because catch doesn't necessarily should go after then.

UnhandledPromiseRejectionWarning means that a promise weren't synchronously chained with catch, this resulted in unhandled rejection.

In async..await, errors should be caught with try..catch:

async function foobar() {
  try {
    await foo() ? console.log("Have foo") : console.log("Not have foo");
  } catch (error) {
    console.error(error);
  }
}

The alternative is to handle errors at top level. If foobar is application entry point and isn't supposed to be chained anywhere else, it's:

foobar().catch(console.error);

The problem with foo is that it doesn't provide meaningful errors. It preferably should be:

if (err || !docs) return reject(err);

Also, most popular callback-based libraries have promise counterparts to avoid new Promise. It mongoist for mongojs.

2
votes

Every solution here just silences the error, but you should probably handle the error instead.

How you handle it depends on the error and on what part of the application you're in. Here are some examples.

You're writing an app

If you're writing a node app and something throws, you might want to use process.exit(1) to quit the app and display the error to the user:

async function init() {
  await doSomethingSerious();
}

init().catch(error => {
  console.error(error);
  process.exit(1)
});

You're writing a module

If the code expects an error, you can catch it and use it as a value instead:

module.exports = async function doesPageExist(page) {
  try {
    await fetchPage(page);
    return true;
  } catch (error) {
    if (error.message === '404') {
      return false;
    }

    // Unrecognized error, throw it again
    throw error;
  }
}

Notice that this example re-throws the error when it's not the expected one. This is fine. It's the final user’s responsibility to handle network errors:

const doesPageExist = require('my-wonderful-page-checker');

async function init() {
  if (await doesPageExist('https://example.com/nope')) {
    console.log('All good')
  } else {
    console.log('Page is missing 💔')
  }
}

// Just like before
init().catch(error => {
  console.error(error);
  process.exit(1)
});

You're the user

If you're seeing this error when using a prepackaged application via command line, like webpack or babel, it might mean that the application had an error but it was not handled. This depends on the application or your input is not correct. Refer to the application’s manual.