My unit test code is like this:
// function for my unit test to test exception
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = () => new Promise((resolve, reject) => {
(async () => {
const res = await mockServiceThrow();
if (res === 1) resolve(1)
else reject(0);
})();
});
createContextAndDoc().catch((e) => {
console.log('--------');
console.log(e.message);
console.log('--------');
});
When i run this unit test:
./node_modules/.bin/jest local_modules/__test__/unhandledException.test.js
The complete output is like this:
RUNS local_modules/__test__/unhandledException.test.js
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: 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(). The promise rejected with the reason "Error: unit test error message".] {
code: 'ERR_UNHANDLED_REJECTION'
}
Don't see why it says unhandled, i do have .catch(). Any suggestions ?
Looks like you're invoking the async function right away, which causes the unit test error, and that is never cought since the promise is never returned - so the .catch is not catching anything. The error is not part of the promise chain.
If you want to invoke the function right away you need to catch the error and reject it so the promise finishes.
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = () => new Promise((resolve, reject) => {
(async () => {
try {
const res = await mockServiceThrow();
if (res === 1) resolve(1)
else reject(0);
} catch (e) {
reject(e)
}
})();
});
createContextAndDoc().catch(e => {
console.log('------------------');
console.log(e.message);
console.log('--------')
})
You could also simplify your code a little:
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = async () => {
const res = await mockServiceThrow();
if(res === 1) {
return Promise.resolve(1)
} else {
return Promise.reject(0);
}
};
createContextAndDoc().catch(e => {
console.log('------------------');
console.log(e.message);
console.log('--------')
})
Edit: Further explanation of promises.
createContextAndDoc is a function that returns a promise. That promise resolves if res === 1 but rejects if res is something else. For that to happen the mockServiceThrow promised must be resolved. Otherwise you won't get any value for res variable.If that happens then your promise function never fullfills (resolves or rejects).
In your case mockServiceThrow fails and throws an error, this error is not part of the promise you created with new Promis. To make sure your promise fullfills (resolves or rejects) you need the callbacks, otherwise the error is not part of the promise.
The simplified code has one async function so that when mockServiceThrow fails its part of the async function that you're trying to catch.
Related
If I have a promise rejection within a for that is not caught by a try/catch surrounding the for or the invoker method catch block
const asyncMethod = async (item) => {
return Promise.reject(item);
};
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
const myMethod = async () => {
try {
const myArr = [1, 2,];
const promises = [];
for (const item of myArr) {
const myPromise = asyncMethod(item);
promises.push(myPromise);
await sleep(1);
}
await Promise.allSettled(promises);
} catch (e) {
console.log('handled by try catch');
}
};
myMethod().catch(() => {
console.log('handled by invoker');
});
If I run this i will get unhandled rejection, and I'm wondering why is it not hitting my catch block.
I'm not asking for a solution, I'm just trying to understand how V8 works.
This isn't just V8 - any spec-compliant implementation will produce an unhandled rejection with this code.
At the moment that a Promise rejects, there must be a .catch handler somewhere that has been attached to the Promise to catch the rejection. Otherwise, an unhandled rejection will occur - the engine can't look into the future to see if you attach a handler at a later time, it must be seen as attached right when the Promise rejects.
Because you do
const myPromise = asyncMethod(item);
promises.push(myPromise);
await sleep(1);
You're waiting for sleep before passing the promise that will reject to .allSettled (which will count as a rejection handler). If the Promise rejects before all the sleeps finish, there'll be an unhandled rejection.
(.allSettled does count as a reject handler, as you can see below:)
const asyncMethod = async (item) => {
return Promise.reject(item);
};
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
const myMethod = async () => {
try {
const myArr = [1, 2,];
const promises = [];
for (const item of myArr) {
const myPromise = asyncMethod(item);
promises.push(myPromise);
// dummy rejection handler through .allSettled attached immediately, just to illustrate:
Promise.allSettled([myPromise]);
await sleep(1);
}
await Promise.allSettled(promises);
} catch (e) {
console.log('handled by try catch');
}
};
myMethod().catch(() => {
console.log('handled by invoker');
});
I'm reading data from db with using await so I used Promise but the function seems to return nothing
async function read() {
return new Promise((resolve, reject) => {
const db = new DB();
db
.read()
.then(result => {
resolve(result);
}).catch(() => {
reject('db-error');
});
});
}
(async () => {
const data = await read();
console.log(data); // undefined
})();
How can I make read() return result?
You are making it more complicated than it has to be. If you are already using an API that returns a promise then there is no need to use the promise constructor yourself.
And declaring a function as async is only necessary if you are using await in it to deal with promises.
So either do:
function read() {
const db = new DB();
return db
.read()
.catch(() => {
return 'db-error';
});
}
Or
async function read() {
const db = new DB();
try {
return await db.read();
} catch(error) {
return 'db-error';
}
}
If you are still not getting the value you want then you are not using the database API correctly and you have to read its documentation to figure out how to get back the right data.
The awesome guys who write the MDN Web Docs say that the result of await will be undefined if the promise that is being waited on is rejected: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#handling_rejected_promises
Check out the following scenario.
This is a simple function that returns a Promise:
function asyncFunc(waitTime) {
return new Promise((resolve, reject) => {
setTimeout(() => {
// say we prefer people who do things in 3 seconds or less
if (waitTime <= 3000) {
resolve('Promise resolved! You\'re fast! :)');
} else {
reject('Promise rejected! You\'re slow! :(');
}
}, waitTime);
});
}
Let's test the function using a method similar to yours:
async function testAsyncFunc(waitTime) {
try {
const result = await asyncFunc(waitTime);
console.log(result);
} catch(error) {
console.error(error.message);
}
}
testAsyncFunc(3000); // Returns `Promise resolved! You're fast! :)`, as expected
testAsyncFunc(3001); // Returns `undefined` instead of `Promise rejected! You're slow! :(`
But since we want the actual rejection error of the asynchronous operation instead of undefined, the solution is to chain catch to the await statement to catch any rejection errors immediately you call the asynchronous function and then throw the error so it can be caught by any catch error handler you may want to use, like so:
async function testAsyncFunc(waitTime) {
try {
const result = await asyncFunc(waitTime)
.catch(error => {
// throw the rejection error so it can be handled by the catch block below
throw new Error(error);
});
// if no errors
console.log(result);
} catch(error) {
console.error(error.message);
}
}
testAsyncFunc(3001); // Returns the expected result: `Promise rejected! You're slow! :(`
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.
Getting an exception in my Node JS Express application and cannot figure out why. I was getting the same error with my "real" code, so I found this code online and made a test router to see if the error occurred with known good code. Code runs fine in plnkr without the router.post line. Could this be because of the function in the first line? Any help is greatly appreciated.
router.post('/addTableTest', (function (req, res, next) {
let promise1 = new Promise((resolve, reject) => {
let data = false;
if (data) {
resolve('Data');
}
if (!data) {
reject('Not Data');
}
})
promise1.then((message) => {
console.log(message);
}).catch((message) => {
console.log(message);
})
}));
The closure executed by new Promise() is executed synchronously, i.e. immediately after the Promise has been created and before new returns. As your closure has been written to fail immediately and you can't attach a .catch() to it before new returns, you get an unhandled Promise rejection exception.
To make your code work you need to
start a Promise chain by creating a resolved Promise
attach a .then() clause to wrap your synchronous code
replace resolve(X) with return X
replace reject(X) with throw new Error(X)
Now you can safely attach the other Promise clauses, because the code in the just created Promise chain won't be executed until the closure that has created it leaves.
router.post('/addTableTest', (req, res, next) => {
let promise1 = Promise.resolve()
.then(() => {
let data = false; // i.e. the promise will reject
if (data) {
return 'Data';
} else {
throw new Error('Not Data');
}
});
promise1
.then(message => {
console.log(message);
})
.catch(error => {
console.log(error.message);
});
});
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().