Reputation: 384
I am working on a project that involves updating the entire mongoDB collection everyday. I am making multiple asynchronous calls so I decide to use async/await and try/catch. My code looks like this:
const updateMongoData = async () => {
try {
const data = await getData(); //This is the new data that I am using to update Mongo docs
const countries = await GlobalData.find();
countries.forEach(async (row) => {
const res = await Global.updateOne(
{ country: row.country },
{
//Use data
lastUpdated: Date.now(),
}
);
});
} catch (err) {
console.log(err);
}
};
Everything works fine except if I make a syntax error e.g Dated.now()
instead of Date.now()
. This is will give my an error saying
(node:8248) 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 368)
I tried inserting another try catch instead my forEach and moved the forEach inside and this seems to catch the syntax error. Why does this work? And what is the cleanest way to do this?
GlobalData.find()
.then(countries => {
countries.forEach(async (row) => {
try{
const res = await Global.updateOne(
{ country: row.country },
{
//Use data
lastUpdated: Date.now(),
}
);
}
catch(err){
console.log(err);
}
})
}).catch(err => console.log(err));
Upvotes: 1
Views: 340
Reputation: 2452
Your try
catch
is unable to catch the ReferenceError
thrown by changing Date
to Dated
because catch
requires a call to the implicit async/await Promise chain's reject
handler. Errors thrown inside Array.prototype.forEach
will not see that handler.
You can think about Array.prototype.forEach
as a method that starts off a whole slew of runaway executions; none of which are meant to be awaited from the parent scope
You can use Promise.all
and Array.prototype.map
to catch the error instead.
const updateMongoData = async () => {
try {
const data = await getData(); //This is the new data that I am using to update Mongo docs
const countries = await GlobalData.find();
await Promise.all(countries.map(async (row) => {
const res = await Global.updateOne(
{ country: row.country },
{
//Use data
lastUpdated: Dated.now(),
}
);
}))
} catch (err) {
console.log('yo', err);
}
};
updateMongoData() // > yo ReferenceError: Dated is not defined
Your second example works because the try
catch
block is sharing the same scope as the Array.prototype.forEach
block. If you moved that try
catch
statement outside, however, it will not see the error.
There is another way using a project I started. Contributions are welcome.
const { pipe, fork, map, tryCatch } = require('rubico')
const updateGlobalByCountry = (country, data) => Global.updateOne(
{ country },
{ lastUpdated: Date.now() }, // <- use data
)
const updateMongoData = tryCatch(
pipe([
fork({
data: getData, // () => data
countries: GlobalData.find, // () => countries
}), // () => ({ data, countries })
({ data, countries }) => map(
country => updateGlobalByCountry(country, data)
)(countries), // ({ data, countries }) => updatedCountries
]),
err => console.log(err),
)
Upvotes: 3