I have the following code, it works but it still throws the warning.
I'm running in Node v12.
(node:15985) 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: 3)
Here is the source code for the loop, thanks:
const links = await knex('links').where('active', true);
const links = await knex('links').where('active', true).catch((e) => console.log(e)) // does not work neither
for (let index = 0; index < links.length; index++) {
const element = links[index];
console.log('Running with', index, element.uri);
(async () => {
try {
const { statusCode } = await got({
url: `${element.protocol}://${element.uri}:${element.port}`,
method: 'GET',
timeout: 5000
})
const check = {
statusCode: statusCode,
}
await knex('checks').insert(check);
} catch (error) {
const check = {
status: 'error',
}
await knex('checks').insert(check);
}
})().catch(() => {});
}
You have 2 promises without a catch block:
As pointed by #Phix in the comment, one on the first line:
// On the first line
await knex('links').where('active', true)
In the catch block at the end of the code:
await knex('checks').insert(check);
I made some change to the original code according to my understanding. I also suspect await knex('checks').insert(check); is the issue for the unhandled promise. So I added a try & catch to handle it. The finally is not required. I just want to make the flow a bit clear. I hope it solve the issue, at least provide some ideas.
// assign the async salad to a const to make the flow a bit more clear
const fetchData = async () => {
try {
let check;
const { statusCode } = await got({
url: `${element.protocol}://${element.uri}:${element.port}`,
method: 'GET',
timeout: 5000
})
check = {
statusCode: statusCode,
};
} catch (error) {
check = {
status: 'error',
}
} finally {
// use a try catch to handle the potential async call
try {
await knex('checks').insert(check);
} catch {
throw new Error('Error')
}
}
};
// if await knex('checks').insert(check) fails, we can catch the error it throws
try {
fetchData()
} catch (erroe) {
console.log(error);
}
Related
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");
// }
};
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 3 years ago.
I have the following code writed in nodejs express and firebase
route.js
try{
const test = await invoiceData.setAssignsInvoiced();
res.json({
status: true,
message: "Successful Invoice Generation"
});
}catch (e) {
res.status(500).json({
status: false,
message: "Internal Server Error",
data: e
});
}
InvoicesStorage.js
setAssignsInvoiced = async() => {
return new Promise(async (resolve,reject)=>{
try {
await _getAssignsForInvoiced(this);
this.assignsForInvoiced.forEach(async assing => {
let aux = assing._key.path.segments.length;
let ref = assing._key.path.segments[aux - 1];
await _updateAssignsToInvoiced(assing.data(),ref);
});
resolve(true)
} catch (error) {
console.error(error)
reject(error)
}
})
};
const _updateAssignsToInvoiced = async (assing, ref) => {
try {
const { invoiceNum } = assing.data(); //Here's an intentional error
await db
.collection("leadAsign")
.doc(ref)
.update({
invoiced: true,
updateDate: Date.now() - 240 * 60 * 1000,
invoiceNum
});
} catch (error) {
console.error(error);
throw new Error("Error at update to invoiced assigns");
}
};
How I hope it works:
According to me, I should throw out a synchronous error because my code has "await" and stop the system.
The answer I have:
the code runs asynchronously, that is, after calling the function the "await" has no effect and answers a "res.json" with status 200 and it is only after it throws the next error.
TypeError: assing.data is not a function
at _updateAssignsToInvoiced (D:\$Workzone\gd_fridays_h\src\controllers\invoices\InvoicesStorage.js:90:35)
at D:\$Workzone\gd_fridays_h\src\controllers\invoices\InvoicesStorage.js:55:23
at Array.forEach (<anonymous>)
at D:\$Workzone\gd_fridays_h\src\controllers\invoices\InvoicesStorage.js:51:37
true
POST /generateSingle 200 5182.650 ms - 57
(node:5600) UnhandledPromiseRejectionWarning: Error: Error at update to invoiced assigns
at _updateAssignsToInvoiced (D:\$Workzone\gd_fridays_h\src\controllers\invoices\InvoicesStorage.js:102:11)
at D:\$Workzone\gd_fridays_h\src\controllers\invoices\InvoicesStorage.js:55:23
at Array.forEach (<anonymous>)
at D:\$Workzone\gd_fridays_h\src\controllers\invoices\InvoicesStorage.js:51:37
(node:5600) 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: 4)
(node:5600) [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.
async/await doesn't work as you're expecting it to inside a forEach loop. An abundance of info on that specific issue here: https://stackoverflow.com/a/37576787/4043746
To fix your problem, you could use a for/of loop:
setAssignsInvoiced = async () => {
return new Promise(async (resolve, reject) => {
try {
await _getAssignsForInvoiced(this)
for (const assign of this.assignsForInvoiced) {
let aux = assign._key.path.segments.length
let ref = assign._key.path.segments[aux - 1]
await _updateAssignsToInvoiced(assign.data(), ref)
}
resolve(true)
} catch (error) {
console.error(error)
reject(error)
}
})
}
However, I'd also be tempted to suggest not returning a promise, as you're essentially doing that due to it being an async function. Something like this should work and is cleaner imo:
setAssignsInvoiced = async () => {
try {
await _getAssignsForInvoiced(this)
for (const assign of this.assignsForInvoiced) {
let aux = assign._key.path.segments.length
let ref = assign._key.path.segments[aux - 1]
await _updateAssignsToInvoiced(assign.data(), ref)
}
} catch (error) {
console.error(error)
// Re-throwing the error to pass the error down, just like you've
// done inside your _updateAssignsToInvoiced function's catch
throw new Error('Error setting assigns')
}
}
Async/await inside a forEach() loop will not wait until all the async operations inside the loop is completed.
One approach would be using Promise.all() like so:
const setAssignsInvoiced = async () => {
try {
await _getAssignsForInvoiced(this);
await _updateAssignsList(this.assignsForInvoiced);
return true;
} catch (error) {
console.error(error);
return new Error(error);
}
};
const _updateAssignsList = assignsList => {
return Promise.all(
assignsList.map(async assign => {
let aux = assign._key.path.segments.length;
let ref = assign._key.path.segments[aux - 1];
return await _updateAssignsToInvoiced(assign.data(), ref);
})
);
};
I've just extracted the async loop process to a separate function which return a Promise.
im trying to get the promise error, and throw to the "try catch" to concentrate the return of the error in one place.
like this:
async schedule(req, res) {
try {
//here is the function that returns a promise
service.search()
.then(async data => {
if (data.length > 0) {
res.status(200).json("OK!");
}
})
.catch(async error => {
//here i want to throw this error to the "try catch" to return the error message
throw new Error(error);
})
}
catch (error) {
res.status(400).json(error);
};
}
but when goes to "throw new Error(error);" gives me the message:
(node:15720) 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)
warning.js:27
someone could help me to understand what im doing wrong?
thank so much!
Rafael
UPDATE
based on the Marcos answer, i did:
async schedule(req, res) {
try {
const data = await service.search();
if (data.length > 0) {
res.status(200).json("OK!");
}
}
catch (error) {
res.status(400).json(error);
};
}
and worked... Now i understand how to handle this errors... thanks!
You either use async/await with a try/catch or .then/.catch, you don't mix both ways.
async schedule(req, res) {
try {
//here is the function that returns a promise
// If service.search rejects, it will go to the `catch`
const data = await service.search()
if (data.length > 0) {
return res.status(200).json("OK!");
}
// do something here
// res.status(400).send('Invalid data')
// throw new Error('Invalid data')
} catch (error) {
res.status(400).json(error);
}
}
or
schedule(req, res) {
service.search()
.then(data => {
if (data.length > 0) {
res.status(200).json("OK!");
}
})
.catch(error => {
res.status(400).json(error);
})
}
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);
My node process exited after logging
E callback: [Function: RP$callback],
E { serviceName: '....',
E errno: 'EAI_AGAIN',
E name: 'RequestError',
But I thought the below code would catch exceptions and return the defaulted value
var rp = require('request-promise');
async function verifyJWT(jwt: string): Promise<any> {
const userDataPromise = rp({
uri: ...,
method: 'POST',
json: {
...
}
});
const userData = await userDataPromise.catch((err: any) => {
console.error(`verifyJWT: err`, err)
return {
authInfo: {}
};
});
return userData;
}
export default verifyJWT
Are there certain types of failures that would not get caught here?
Would it be better to provide a rejection handler in catch on the promise?
Thanks,
Brent
You're mixing standard promise handling with async/await style promise handling. You should try to use one or the other, but not both. Using both methods simultaneously is not restricted, but it certainly adds confusion.
This is what async/await style promise handling should look like:
async function verifyJWT(jwt: string): Promise<any> {
try {
return await rp({
uri: ...,
method: 'POST',
json: {
...
}
});
} catch (err) {
console.error(`verifyJWT: err`, err);
return {
authInfo: {}
};
}
}
This is what standard promise handling should look like:
function verifyJWT(jwt: string): Promise<any> {
return rp({
uri: ...,
method: 'POST',
json: {
...
}
}).catch((err: any) => {
console.error(`verifyJWT: err`, err);
return {
authInfo: {}
};
});
}
You aren't using async await as it should be used
async await enables the code to wait for promise response and then move on to the next step. Any error returned by the rest endpoint, is caught by the catch block.
i've posted a sample code below, to show how async await would work with promise
var rp = require('request-promise');
var myfun = async ()=>{
let returnValue = null;
try{
let response = await rp("http://localhost:3000/getdata");
console.log("ajax success");
returnValue = response;
}
catch(err){
console.log("ajax error ",err.message)
returnValue = err.message;
}
return returnValue;
}
Note if you want to return any data from async function, then the calling function should also be async and await has to be used while invoking.
Read more about async await