How can I catch error rethrown from async function? - javascript

try {
const promise = new Promise(async (resolve, reject) => {
reject('oe')
})
.catch(async (err) => {
console.log('bbbbb', err)
throw err
})
} catch (err) {
console.log('aaaaa', err)
}
Is is possible to make aaaaa loggable

In general, it doesn't make sense to pass an async function into a promise's then or catch function. And it never makes sense to pass one into the promise constructor. If you're going to go async, do it earlier. Also, when you want a rejected promise for testing, etc., just use Promise.reject('oe').
In order to catch an error from an async function with a try/catch, you must be in an async function. In that case, the minimal change to your example would be to await the result of the call to catch:
// Assuming this is in an `async` function, making only minimal changes
try {
const promise = new Promise(async (resolve, reject) => {
reject('oe')
})
.catch(async (err) => {
console.log('bbbbb', err)
throw err
})
await promise; // ***
} catch (err) {
console.log('aaaaa', err)
}
If you aren't in an async function, you can't use try/catch to catch errors from promises (which includes from an async function call, since they return promises). Instead, you have to use the promise returned by catch:
// Assuming this is NOT in an `async` function, making only minimal changes
const promise = new Promise(async (resolve, reject) => {
reject('oe')
})
.catch(async (err) => {
console.log('bbbbb', err)
throw err
})
.catch(err => {
console.log('aaaaa', err)
})
Making larger changes, if you're already in an async function, get rid of the then and catch calls:
// Assuming this is in an `async` function
try {
try {
await Promise.reject('oe');
} catch (innerError) {
console.log('bbbbb', innerError);
throw innerError;
}
} catch (outerError) {
console.log('aaaaa', outerError);
}

You can use async/await:
async function test() {
try {
const promise = await new Promise(async (resolve, reject) => {
reject('oe')
}).catch(async err => {
console.log('bbbbb', err)
throw err
})
} catch (err) {
console.log('aaaaa', err)
}
}
test()

Related

How to handle reject promise error in outer try catch with inner Promise.all?

When an error/rejection occurs in detectingDog or detectingDog, the error is successfully handled by the .catch(error of the Promise.all() but I want the error to be directly handled by the catch (err) of the try structure.
How can I do this ?
PS: I have already tried to get rid of the .catch(error but then the Promise.all() hangs forever
try {
function detectingDog(bufferedData) {
return new Promise((resolve, reject) => {
package.detectDog(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
function detectingCat(bufferedData) {
return new Promise((resolve, reject) => {
package.detectCat(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
Promise.all([
detectingDog(param1),
detectingCat(param2)
]).then(responseData => {
callback(undefined, responseData);
}).catch(error => {
// (1) I need to pass the error to the outer structure where error handling is done
});
} catch (err) {
console.log(err);
// handing of the inner error (2) here
callback(err);
}
Thanks!
...but I want the error to be directly handled by the catch (err) of the try structure.
You can't do that in a non-async function, because control has already left the try/catch by the time that rejection occurs, which is after whatever function this code is in (if any) has returned.
In an async function, you can use await on a promise, which will make a rejection throw, so it would go to your try/catch. So you could do the following, but keep reading because it's fairly odd:
// In an `async` function
try {
function detectingDog(bufferedData) {
return new Promise((resolve, reject) => {
package.detectDog(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
function detectingCat(bufferedData) {
return new Promise((resolve, reject) => {
package.detectCat(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
const responseData = await Promise.all([
detectingDog(param1),
detectingCat(param2)
]);
callback(responseData);
} catch (err) {
console.log(err);
callback(err);
}
...but it doesn't make a lot of sense to go to the trouble of converting callback APIs to promises if you're just going to provide a callback-based API to your caller. Just return a promise. That makes the whole try/catch disappear:
// No need for these to be nested
function detectingDog(bufferedData) {
return new Promise((resolve, reject) => {
package.detectDog(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
resolve(data); // No need for `return`
}
});
});
}
function detectingCat(bufferedData) {
return new Promise((resolve, reject) => {
package.detectCat(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
function example(param1, param2) {
return Promise.all([
detectingDog(param1),
detectingCat(param2)
]);
}
You have two options here.
If you really need the try/catch block you will need to run your code in an async function, leveraging the fact that awaiting a rejected Promise will throw an error in this context:
(async function () { // you might not need the immediately invoking function wrapper depending on your context
try {
function one(bufferedData) {
// return a promise
}
function two(bufferedData) {
// return a Promise
}
const responseData = await Promise.all([
one(param1),
two(param2)
])
callback(undefined, responseData)
} catch (err) {
console.log(err);
// handing of the inner error (2) here
callback(err)
}
})()
Alternatively, you can also just handle the error in the catch block of your Promise chain:
function one(bufferedData) {
// return a promise
}
function two(bufferedData) {
// return a Promise
}
Promise.all([
one(param1),
two(param2)
])
.then((responseData) => {
callback(undefined, responseData)
})
.catch((err) => {
console.log(err);
// handing of the inner error (2) here
callback(err)
})

Promisify Express response.render method

I need to create a render method to build html blocks into a string.
It seems to work but I used the notorious "new Promise" and I wanted to know if what I've done is either correct or not:
async render(req, res) {
const locals = await this.execute(req); // DB operation, retrieve context
return new Promise((resolve, reject) => {
try {
return res.render('my-view', { ...locals, layout: null }, (err, html) => {
if (err) {
return reject(err);
}
return resolve(html);
});
} catch (err) {
return reject(err);
}
});
}
Thank you!
The new Promise constructor implicitly catches (synchronous) exceptions from the executor callback, so that try/catch is not needed. Also, the return value is ignored. You'd write just
async render(req, res) {
const locals = await this.execute(req); // DB operation, retrieve context
return new Promise((resolve, reject) => {
res.render('my-view', { ...locals, layout: null }, (err, html) => {
if (err) reject(err);
else resolve(html);
});
});
}

UnhandledPromiseRejectionWarning in protractor

I'm trying to retrieve values from mongoDB and its giving me the
UnhandledPromiseRejectionWarning: MongoError: topology was destroyed
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().
[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.
Following is the scaled down code version
CLASS 1
connectToMongoDatabase() {
try {
return new Promise((resolve, reject) => {
mongoclient.connect('mongodb://************************', (err, db) => {
if (err) {
reject(err);
}
else {
resolve(db);
}
});
});
}
catch (err) {
console.log(err);
}
}
fetchIssuesFromMongo(dbName, collectionName, query, db) {
try {
let dbo = db.db(dbName);
return new Promise((resolve, reject) => {
let collection = dbo.collection(collectionName);
collection.find(query, (err, result) => {
if (err) {
reject(err);
}
else {
resolve(result);
dbo.close();
}
});
});
}
catch (err) {
console.log(err);
}
}
CLASS 2
executeQuery(issueCount){
this.CLASS1.connectToMongoDatabase().then((db) => {
this.CLASS1.fetchIssuesFromMongo(dbName, collectionName, query, db).then((result: any) => {
expect(result.count()).toEqual(issueCount);
});
});
}
SPEC FILE
it('verify result', (done) => {
CLASS2.executeQuery(6).then(() => {
done();
});
});
What I think is the test fails after this.CLASS1.connectToMongoDatabase().
Is there any issue with how I'm using promises ? I'm resolving all the promises and have reject statements also in place.
Any suggestions ?
Updating your Class 1
Remove the try catch since it will never catch on a returned promise. Here's the change for fetchIssuesFromMongo. You should do something similar for connectToMongoDatabase
fetchIssuesFromMongo(dbName, collectionName, query, db) {
const dbo = db.db(dbName);
return new Promise((resolve, reject) => {
const collection = dbo.collection(collectionName);
collection.find(query, (err, result) => {
if (err) {
reject(err); // at this point you should call a .catch
} else {
dbo.close(); // switching the order so the close actually happens.
// if you want this to close at the exit, you should
// probably not do it like this.
resolve(result);
}
});
});
}
Fixing the executeQuery in Class 2
In your executQuery:
executeQuery(issueCount){
// if connectToMongoDatabase is thenable, then you should also call .catch
// you should also return a promise here so your Protractor code can actually
// call .then in `CLASS2.executeQuery(6).then`
return this.CLASS1.connectToMongoDatabase().then((db) => {
this.CLASS1.fetchIssuesFromMongo(dbName, collectionName, query, db).then((result: any) => {
expect(result.count()).toEqual(issueCount);
}).catch(e => {
console.log(e);
});
}).catch(e => {
console.log(e);
});
}
Think about using async / await.
This usually helps clear up the nested chain of promises. I prefer this.
// this returns implicitly returns a Promise<void>
async executeQuery(issueCount) {
// valid to use try catch
try {
const db = await this.CLASS1.connectToMongoDatabase();
const result = await this.CLASS1.fetchIssuesFromMongo(dbName, collectionName, query, db);
expect(result.count()).toEqual(issueCount);
} catch(e) {
console.log(e);
}
}
Use async / await in your Protractor test
Finally in your Protractor test you should turn off the selenium promise manager. This is something you'll do in your configuration file. SELENIUM_PROMISE_MANAGER: false,
Next you can use async / wait in your test.
it('verify result', async () => {
await CLASS2.executeQuery(6);
});
I'm not a fan of expecting a condition in your class and it might be better to return the value from class 2. So I would maybe return a Promise from executeQuery.
const issueCount = 6;
const queryResult = await CLASS2.executeQuery(issueCount);
expect(queryResult).toEqual(issueCount);
Hope that helps.

Async Javascript function returns undefined

I am trying to run this function but the it keeps returning undefined when I explicitly hardcode the return value.
const splitVideo = async (sid, video, part) => {
let framesLocation =`${process.cwd()}/${dirs.videoFrames}/${sid}_${part}`;
console.log(fs.existsSync(framesLocation));
if(!fs.existsSync(framesLocation)) {
console.log("making dir");
f.s.mkdirSync(framesLocation);
}
ffmpeg(video)
.on('end', () => {
return "done";
})
.on('error', (err) => {
throw err;
})
.screenshots({
timestamps: [1,2],
filename: `${sid}_${part}/frame-%s.png`,
folder: `${process.cwd()}/${dirs.videoFrames}`
});
};
Please help this is very frustrating.
Your function does not return anything, thats why you are getting undefined. Wrap the ffmpeg call in new Promise(...) to be able to resolve its asynchronous result:
const splitVideo = async (sid, video, part) => {
// ...
return new Promise((resolve, reject) => {
ffmpeg(video)
.on('end', () => {
resolve("done");
})
.on('error', (err) => {
reject(err);
})
.screenshots({
timestamps: [1,2],
filename: `${sid}_${part}/frame-%s.png`,
folder: `${process.cwd()}/${dirs.videoFrames}`
});
};
};
const ret = await splitVideo(...);
console.log(ret);
Also note you need to await this function to be able to read the result (or get the result in then handler).
You can only await promises.
const splitVideo = async (sid, video, part) => {
// ...
const val = await new Promise((resolve, reject) => {
ffmpeg(video)
.on('end', () => {
resolve("done");
})
.on('error', (err) => {
reject(err);
})
.screenshots({
timestamps: [1,2],
filename: `${sid}_${part}/frame-%s.png`,
folder: `${process.cwd()}/${dirs.videoFrames}`
});
//... more code using the val maybe?
return val
};
};
The problem is that your ffmpeg(video) function inside your splitVideo async function is asynchronous. What's happening is that your splitVideo function is being called but it's returning undefined before your ffmpeg(video) function accomplishs. What can you do to resolve this?
You already defined your splitVideo function as an async function, this allows you to use the reserved word "await". But first let's encapsulate your ffmpeg(video) function inside a promise.
const splitVideo = async (sid, video, part) => {
//Your code
let promise = new Promise((resolve, reject) => {
ffmpeg(video)
.on('end', () => {
resolve("done");
})
.on('error', (err) => {
reject(err);
})
.screenshots({
timestamps: [1,2],
filename: `${sid}_${part}/frame-%s.png`,
folder: `${process.cwd()}/${dirs.videoFrames}`
});
});
try{
return await promise;
}catch(err){
return err;
}
This should be enough for your splitVideo function, but it's importante to pay attention cause unhandled promises rejections are deprecated and in the future will terminate your node.js process. What you need to do is also add a .catch() statement to you splitVideoFunction, something like:
splitVideo.catch((err) => {//your code}).then((result) => {//Your code})
Or your can call splitVideo inside an another async function and use:
try {
await splitVideo(video);
}catch(err){
//Your code
}
If you have any doubt about async/await like I had, you may find this question useful.
Node.JS - Can`t get async throws with try/catch blocks
I hope it helps.

Promise reject() in async function causes “Uncaught (in promise)” warning

The following code snippet causes an "Uncaught exception warning" when reject(err) is called, although the method is called inside a try/catch statement.
async verifyToken (token: string): Promise<string | object> {
return new Promise<string | object>( (resolve, reject) => {
jwt.verify(token, this._publicKey, (err, decoded) => {
if (err) {
reject(err);
} else {
resolve(decoded);
};
});
}
This violates my understanding about promisses and the async/await pattern, because up to now I do not expect an unhandling error if the function is called inside a try/catch statement.
try {
const obj = await verifyToken(token);
} catch (err) {
...
}
At the moment I avoid this problem with a work arround.
async verifyToken (token: string): Promise<string | object> {
let cause: any;
const rv = await new Promise<string | object>( (resolve, reject) => {
jwt.verify(token, this._publicKey, (err, decoded) => {
if (err) {
cause = err;
resolve(undefined);
} else {
resolve(decoded);
};
})
});
if (rv) {
return rv;
} else if (cause instanceof Error) {
throw cause;
} else {
throw new Error('invalid token');
}
}
My questions:
Why does the catch not solve this problem?
Is there any better solution to avoid an unhandled error in promisses inside an async function?
I know this is old, and I'm sorry but I just stumbled upon this a moment ago searching for something else, but I think I have the answer to this specific problem.
Promise will neither resolve nor reject in the cast that jwt.verify() throws its own error. Guessing that the lib is jsonwebtoken or something like that I suspect that you have passed an invalid token and that the method does a throw new Error('Invalid token')
I think further to that you're mixing some ideas if you are going to return a new Promise you likely shouldn't use the async keyword.
Suggestion,
function verifyToken(token: string): Promise<ABetterType> {
return new Promise((resolve, reject) => {
try {
jwt.verify(token, this._publicKey, (err, decoded) => {
if (err) {
reject(err)
return;
}
resolve(decoded)
}
} catch (err) {
reject(err)
}
});
}
OR (what I think I would've tried)
function verifyToken(token: string): Promise<ABetterType> {
return new Promise((resolve, reject) => {
try {
const decoded = jwt.verify(token, this._publicKey);
resolve(decoded);
} catch (err) {
reject(err);
}
});
}
with async/await:
//some async function
try {
let response = await getAllPosts();
} catch(e) {`enter code here`
console.log(e);
}
More here: "Uncaught (in promise) undefined" error when using with=location in Facebook Graph API query

Categories