Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unhandle rejection promise async await chain

I'm fairly new to async await in javascript so this question might be something I don't know.

I have this

  async function foo(req, res, next) {
    try {
      await scan(req.params.stack);
      res.send('ok');
    } catch (err) {
      res.status(500).send('fail');
    }
  }

async function scan(stack) {
  try {
    const libs = [1,2,3];
    const promises = libs.map(async l => analyze(stack, l)
      .catch((err) => { throw new Error(err); }));
    return q.allSettled(promises)
      .then((results) => {
        const rejected = results.filter(r => r.state === 'rejected');
        if (rejected.length === results.length) throw new Error('Failed');
        return results;
      })
      .catch((err) => {
        throw new Error(err);
      });
  } catch (err) {
    throw new Error(err);
  }
}

async function analyze(stack, libraries) {
  try {
    const config = await buildConfiguration(stack, libraries);
    return await databaseInsertion(vulnsObject);
  } catch (err) {
    return Promise.reject('Error while trying to analyze libs');
  }
}

Somehow I'm getting this wild warning and I don't know where I am not catching the error.

Of course, I'm making build configuration fail in order to test the error, but instead of having a normal flow cathing the error I got this:

(node:415) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): Error: Error while trying to analyze libs

Am I using async await good? Is there any pattern I should follow in order to chain async await?

The wild thing is that the foo function works well, meaning that the res.status.(500).send('fail'); works and I'm getting the response

When I was using native promises this error didn't appear.

I'm really stuck here

like image 497
Julian Mendez Avatar asked Sep 17 '18 18:09

Julian Mendez


People also ask

How do you handle unhandled promise rejection?

If an error condition arises inside a promise, you “reject” the promise by calling the reject() function with an error. To handle a promise rejection, you pass a callback to the catch() function. This is a simple example, so catching the rejection is trivial.

How do you get a promise rejection in async await?

You can handle rejected promises without a try block by chaining a catch() handler before awaiting the promise.

How do I resolve a nested promise?

In a promise nesting when you return a promise inside a then method, and if the returned promise is already resolved/rejected, it will immediately call the subsequent then/catch method, if not it will wait. If promised is not return, it will execute parallelly.

How do you handle error in async await in node JS?

If we want to handle the error for asynchronous code in Node. js then we can do it in the following two manners. Handle error using callback: A callback function is to perform some operation after the function execution is completed. We can call our callback function after an asynchronous operation is completed.


2 Answers

While using async-await scan function you were mixing .then() .catch() waterfall with await. async-await handles promises as good as .then(). So stick with one flow and try to mix both in one function or one inside another.

async foo(req, res, next) {
    try {
        await scan(req.params.stack);
        res.send('ok');
    } catch (err) {
        res.status(500).send('fail');
    }
}

async scan(stack) {
  try {
    const libs = [1,2,3];

    // This libs.map functions return promise. then why not use await?
    const promises = await libs.map(async l => analyze(stack, l);

    // Again q.allSettled returns promise, use await here too
    let results = await q.allSettled(promises);

    const rejected = results.filter(r => r.state === 'rejected');

    if (rejected.length === results.length) throw new Error('Failed');

    return results;

  }

  // If any promise call reject function will be in catch
  catch (err) {
    throw new Error(err);
  }
}

async function analyze(stack, libraries) {
    try {
        const config = await buildConfiguration(stack, libraries);
        return await databaseInsertion(vulnsObject);
    }
    catch (err) {
        console.log(err);
        return null;
    }
}
like image 107
NAVIN Avatar answered Oct 20 '22 00:10

NAVIN


Calling an async function (here, analyze) would return a promise, which will resolve or reject according to the return value of the async function or whether an error was thrown.

Now, the analyze function is handling the error thrown but it will return a Promise.reject() when an error is thrown. A Promise.reject() is the unhandled rejection here, which is what the log is stating.

In terms of a synchronous function the equivalent will be

function sync() {
  try {
    // do something dangerous
  } catch (ex) {
    throw Error('Something bad happened'); // this error is still being thrown and nobody is handling it
  }
}

To handle this error you can do the following when you are calling sync, wrap it in try and catch again

try {
  sync();
} catch (ex) {
  console.error(ex); // not gonna throw another exception, otherwise the program might crash
}

Now, the equivalent of this wrap for the analyze function will be using another async function, or better since calling async function will return a Promise, use the catch method of a Promise

analyze()
  .then(() => console.log('My work is done here'))
  .catch(ex => console.error(ex)); // NOTE: not throwing another exception

Even better would be to not return a rejection from catch in the first place, thus making analyze,

async function analyze(stack, libraries) {
  try {
    const config = await buildConfiguration(stack, libraries);
    return await databaseInsertion(vulnsObject);
  } catch (err) {
    console.error(err); // not eating up good errors with something vague is always good
    return null; // or something else to signify that insert failed
  }
}
like image 29
Akshendra Pratap Avatar answered Oct 20 '22 00:10

Akshendra Pratap