Multiple await call error handling in NodeJS - node.js

I am processing multiple records using async/await and for parallel using Promise.all see my sample code below
let results = [100,200]
let promises = results.map(async record => {
try {
return await processingRecords(record);
} catch (err) {
}
});
await Promise.all(promises);
async function processingRecords(item) {
switch (item['#type']) {
case 'case1':
await Request1(item)
await Request2(item)
break
case 'case2':
await Request3(item)
}
}
But the problem is if Request1 is getting any error I can't catch error from Request2 call how to handle error from both calls

You can to do a few things here to keep the calls going. Catching errors around the await statements and returning the combined result of Request1 and Request2 will work:
For example:
async function processingRecords(item) {
switch (item['#type']) {
case 'case1':
let combinedResult = {};
try {
combinedResult.Request1Result = await Request1(item);
} catch (err) {
combinedResult.Request1Error = err;
}
try {
combinedResult.Request2Result = await Request2(item);
} catch (err) {
combinedResult.Request2Error = err;
}
// Keep the promise chain intact.;
return combinedResult;
case 'case2':
return await Request3(item);
}
}
let promises = results.map(async record => {
try {
return await processingRecords(record);
} catch (err) {
// Keep the promise chain intact by throwing err here.
throw err;
}
});
let overallResult = await Promise.all(promises);
console.log(overallResult);

Related

How does error handling work for async map inside an async function?

I have successfully created a function that creates folders according to a directory.
However, when I have tried to convert the for of into a map() method using Promise.all(), the error was not handled the same.
import { readdir, mkdir } from 'fs/promises';
const copyUsingFor = async (target) => {
try {
const dir = await readdir(`./test`, { withFileTypes: true });
for (const item of dir) {
if (item.isDirectory()) {
try {
await mkdir(`./${target}`);
}
catch (err) {
throw err;
}
}
}
}
catch (err) {
console.log(`desired err handling`);
throw err;
}
}
const copyUsingMap = async (target) => {
try {
const dir = await readdir(`./test`, { withFileTypes: true });
Promise.all(
dir.map(async item => {
if (item.isDirectory()) {
try {
await mkdir(`./${target}`);
}
catch (err) {
throw err;
}
}
})
)
}
catch (err) {
throw err;
}
}
copyUsingFor(`../`).catch(err => console.log(`An error occurred: ${err}`)) // <- Works.
copyUsingMap(`../`).catch(err => console.log(`An error occurred: ${err}`)) // <- Gives "triggerUncaughtException".
I am guessing it is because I have an async inside another, but could not figure out why it does not works the same.
Thanks!
You are missing the await in front of Promise.all, that's all. Because of this, what happens inside the Promise.all is detached from the rest of your program flow.
Side note: catch (err) { throw err } is a no-op. You can get rid of the whole try/catch if you don't do anything other than rethrowing the error in it. It feels a bit like wrapping stuff in a call to a function x => x.

Catch promise rejection in nested function

I have this repro: https://codesandbox.io/s/jolly-bogdan-k6ii4?file=/src/index.ts
code:
const wait = (timeoutMs: number) => {
let timeoutHandle: number | undefined;
const promise = new Promise((_resolve, reject) => {
timeoutHandle = setTimeout(() => {
reject(`wait timed out after ${timeoutMs} ms`);
}, timeoutMs);
});
return {
promise,
cancel: (): void => clearTimeout(timeoutHandle)
};
};
const waitBy = (timeoutMs: number) => {
const res = wait(timeoutMs);
return res;
};
const main = async () => {
try {
const { promise, cancel } = waitBy(3000);
} catch (error) {
console.log("failed on timeout");
}
// try {
// await promise;
// } catch (error) {
// console.log("timed out");
// }
};
main();
When this is ran in Node.js, the reject will throw after 3s, and blow up the whole process with an "unhandledRejection Error" - where can one catch this error to avoid an unhandledRejection Error, but allow it to propagate up to the catch inside the main function?
The problem is that you're not waiting for the promise to resolve before moving forward and the error is thrown then outside of the try block.
const main = async () => {
try {
const { promise, cancel } = waitBy(3000);
await promise // new code
} catch (error) {
console.log("failed on timeout");
}
// try {
// await promise;
// } catch (error) {
// console.log("timed out");
// }
};

Transactions with node pg and foreach loop and async await

I am trying to insert multiple rows in PostgreSQL using node pg. I am using transactions but my query is executing after a response. I tried async await with my function but it is not working
This is my function
addPersons = async (req, res) => {
try {
await db.query("BEGIN");
req.body.forEach((person, index) => {
if (person.id) {
try {
await db.query("ROLLBACK");
} catch (error) {
console.error("Error rolling back client", err.stack);
}
return res
.status(Error_code.IdNotFound.code)
.send(Error_code.IdNotFound);
}
const query = `update person set
name = ${person.name},
where id = '${
person.id
}'`;
try {
await db.query(query);
} catch (error) {
try {
await db.query("ROLLBACK");
} catch (error) {
console.error("Error rolling back client", err.stack);
}
return res.status(500).send(err);
}
})
await db.query("COMMIT");
res.status(Error_code.Successfull.code).send(Error_code.Successfull);
} catch (error) {
try {
db.query("ROLLBACK");
} catch (error) {
console.error("Error rolling back client", err.stack);
}
return res
.status(Error_code.UnableToBeginTransaction.code)
.send(Error_code.UnableToBeginTransaction);
}
}
I also tried calling this function from another function and using foreach on that function but when whenever code detects await or callback in the second function it does not wait and return to the first function.
How can I run this code to add my data into PostgreSQL with transactions
Thanks
Since this is tagged node-postgres, I suggest that you base your code on the A pooled client with async/await example in the node-postgres documentation. I also suggest that you use parameterized queries or a query builder such as mongo-sql. (There are many, but that one's my favourite. 🙂)
It could look something like this:
const { Pool } = require("pg");
const pool = new Pool();
const addPersons = async (req, res) => {
const db = await pool.connect();
try {
await db.query("BEGIN");
const query = `update person set name = $1 where id = $2;`;
// Promise.all() may improve performance here, but I'm not sure if it's safe
// or even useful in the case of transactions.
for (const person of req.body) {
await db.query(query, [person.name, person.id]);
}
await db.query("COMMIT");
} catch (e) {
await db.query("ROLLBACK");
throw e;
} finally {
db.release();
}
};

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
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;
}
}
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
}
}
In the analyze() you are returning Project.reject() but analyze() is an async function. Therefor it resolves any value that you return and rejects any error you throw.
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');
}
}
So when the analyze function catches an error you are creating a rejection but then resolving the function. So Promise.reject('Error while trying to analyze libs'); is not being handled. Since async functions always return a promise that resolves with whatever you return and rejects whatever you throw, your analyze function is always going to resolve. Try doin this...
async function analyze(stack, libraries) {
try {
const config = await buildConfiguration(stack, libraries);
return await databaseInsertion(vulnsObject);
} catch (err) {
throw Error('Error while trying to analyze libs');
}
}
The other thing I see as a possible problem in this code is even though you pass the map(async func) an async function, it doesn't care. It won't wait for each function to complete before calling the next.
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);
});
There are two changes bellow
const promises = libs.map(async l => await analyze(stack, l)
.catch((err) => { throw new Error(err); }));
return q.allSettled( await 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);
});
I added an await before the analyze function and an await before passing the promises variable into q.allSettled().

logging errors in async functions

I have this code:
async function getURL() {
try {
await fetch("http://www.blah.com");
return 0;
} catch (err) {
return err;
}
}
getURL().then( result => {
if (result === 0) console.log("success");
else console.log(result);
});
The fetch will fail and the error is logged to the console. How do I rework the code so it uses async and try/catch everywhere? That is, I'm looking to avoid doing getURL().then for the sake of consistency.
EDIT:
For those downvoting me, await getURL() won't work as it's invalid syntax.
EDIT2:
Tried this but it didn't catch the error:
async function getURL() {
return await fetch("http://www.blah.com");
}
let result = async function() {return await getURL();}
try {
result();
} catch (e) {
console.log(e);
}
You can wrap your whole code inside an instantly executed async function like this:
// service.js
async function getURL() {
return await fetch("http://www.blah.com");
}
// your.module.js
(async function() {
// do things...
try {
let result = await getURL();
} catch (e) {
console.log(e);
}
// do things...
res.send({});
});
Every time you need to catch an error from promise, either using new Promise, async-await or generator you need to use .then() or you can do something like this another async-await.
async function getURL() {
try {
await fetch("http://www.blah.com");
return 0; // EDIT: just returning value which is success
} catch (err) {
return err; // EDIT: returning value not rejecting a promise
}
}
async function main () {
try {
let result = await getURL();
if (result === 0) console.log("success");
console.log(result); // EDIT: error will be print.
}
catch (err) { // EDIT: getURL() never rejects so always success.
console.log(err);
}
});
main();
This situation doesn't really occurs as while our main function in server-side or client-side are async and handling this for us.
Like using express:
app.post('/api', async (req, res) => {
try {
let result = await getURL();
res.send(async);
}
catch(err) {
res.send(err);
}
});
EDIT: asyn-await doesn't reject or resolve a call, just return a value. thus must be used carefully.
function fetch(url) {
return new Promise( (resolve, reject) => {
let x = Math.floor(Math.random() * Math.floor(9) + 1);
// 50-50 resolve or reject
if(x%2===0) return resolve(false); //resolve with `false` statement
reject(true); // reject with `true` still a reject
});
}
async function getURL() {
try {
await fetch("http://www.blah.com");
return 0; // if fetch resolve
} catch (err) { //only if fetch reject
return err;
}
}
async function main () {
try {
let result = getURL();
if (result === 0) console.log("success"); //getURL never reject any call
console.log(result);
}
catch (err) { // getURL doesnt reject
console.log(err);
}
};
main();
I realize now async functions always return a promise. Even if you throw an error it still gets wrapped up into a promise. Therefore using try/catch won't help. This is how I ended up writing the code:
async function getURL() {
return await fetch("http://fake");
}
getURL().then( () => console.log("success")).catch( (e) => console.log(e));

Resources