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

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

Related

escape loop using an error from async function?

let me explain what I mean using an example
async function async_function(){
await new Promise(r=>setTimeout(r,3000));
throw 'task completed'
}
async function do_something_meanwhile() {
await new Promise(r => setTimeout(r, 500));
console.log(Math.floor(Math.random()*10));
}
(async ()=>{
try {
async_function(); //this returns an error after a while
while (...)
await do_something_meanwhile();
} catch (err) { console.log('exited with error:',err) }
console.log('moving on');
})();
I'm trying to run an async function and after it is complete immediately terminate the loop,
the best way I could think of (without any time delay) was to send an error
but it gives this error instead of moving on after it's done:
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 "task
completed".] {
code: 'ERR_UNHANDLED_REJECTION'
}
is there a way around this or a better to achieve the desired effect?
You can handle rejection by setting an error variable that you can check in the loop:
try {
let error;
async_function()
.catch(err => error = err);
while (...) {
if (error) {
throw error;
}
await do_something_meanwhile();
}
} catch (err) {
console.log('exited with error:',err)
}
If you need to proactively tell do_something_meanwhile to terminate as well, you could use an AbortController and pass its signal to do_something_meanwhile.
try {
let error;
const controller = new AbortController();
const { signal } = controller;
async_function()
.catch(err => {
error = err;
controller.abort();
});
while (...) {
if (error) {
throw error;
}
await do_something_meanwhile(signal);
}
} catch (err) {
console.log('exited with error:',err)
}
I think if I were doing that, I might subclass AbortController so I can put the error in it:
class AbortContollerWithError extends AbortController {
abort(error) {
this.error = error;
super.abort();
}
}
then:
try {
const controller = new AbortController();
const { signal } = controller;
async_function()
.catch(err => {
controller.abort(err);
});
while (...) {
if (signal.aborted) {
throw controller.error;
}
await do_something_meanwhile(signal);
}
} catch (err) {
console.log('exited with error:',err)
}
...or something along those lines.
You asked how you'd use the signal in do_something_meanwhile, and suggested in a comment that you're really using a timer in it. That's where the signal's abort event comes in handy, you can use that to settle the promise early:
async function do_something_meanwhile(signal) {
let cancelError = {};
try {
await new Promise((resolve, reject) => {
const timer = setTimeout(resolve, 500);
signal.addEventListener("abort", () => {
clearTimeout(timer);
cancelError = new Error();
reject(cancelError);
});
});
console.log(Math.floor(Math.random() * 10));
} catch (error) {
if (error === cancelError) {
// Probably do nothing
} else {
// Something else went wrong, re-throw
throw error;
}
}
}
Promise.all can run async_function and do_something_meanwhile in parallel mode.
While Promise/A doesn't have a cancel method, you can define a stopFlag, and check it in do_something_meanwhile function and the while loop.
let stopFlag = false
async function async_function() {
await new Promise(r=>setTimeout(r, 3000));
throw 'task completed'
}
async function do_something_meanwhile() {
await new Promise(r => setTimeout(r, 500));
if (!stopFlag) {
console.log(Math.floor(Math.random() * 10));
}
}
(async()=>{
try {
await Promise.all([
async_function().catch((err) => {
stopFlag = true
throw err
}), // this returns an error after a while
(async () => {
while (!stopFlag)
await do_something_meanwhile();
})()
])
} catch (err) {
console.log('exited with error:', err)
}
console.log('moving on');
})();

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.

How can I catch error rethrown from async function?

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()

Throw custom errors async/await try-catch

Let's say I have a function like this -
doSomeOperation = async () => {
try {
let result = await DoSomething.mightCauseException();
if (result.invalidState) {
throw new Error("Invalid State error");
}
return result;
} catch (error) {
ExceptionLogger.log(error);
throw new Error("Error performing operation");
}
};
Here the DoSomething.mightCauseException is an asynchronous call that might cause an exception and I'm using try..catch to handle it. But then using the result obtained, I might decide that I need to tell the caller of doSomeOperation that the operation has failed with a certain reason.
In the above function, the Error I'm throwing is caught by the catch block and only a generic Error gets thrown back to the caller of doSomeOperation.
Caller of doSomeOperation might be doing something like this -
doSomeOperation()
.then((result) => console.log("Success"))
.catch((error) => console.log("Failed", error.message))
My custom error never gets here.
This pattern can be used when building Express apps. The route handler would call some function which might want to fail in different ways and let the client know why it failed.
I'm wondering how this can be done? Is there any other pattern to follow here? Thanks!
Just change the order of your lines.
doSomeOperation = async() => {
let result = false;
try {
result = await DoSomething.mightCauseException();
} catch (error) {
ExceptionLogger.log(error);
throw new Error("Error performing operation");
}
if (!result || result.invalidState) {
throw new Error("Invalid State error");
}
return result;
};
Update 1
Or you could create custom errors as below.
class MyError extends Error {
constructor(m) {
super(m);
}
}
function x() {
try {
throw new MyError("Wasted");
} catch (err) {
if (err instanceof MyError) {
throw err;
} else {
throw new Error("Bummer");
}
}
}
x();
Update 2
Mapping this to your case,
class MyError extends Error {
constructor(m) {
super(m);
}
}
doSomeOperation = async() => {
try {
let result = await mightCauseException();
if (result.invalidState) {
throw new MyError("Invalid State error");
}
return result;
} catch (error) {
if (error instanceof MyError) {
throw error;
}
throw new Error("Error performing operation");
}
};
async function mightCauseException() {
let random = Math.floor(Math.random() * 1000);
if (random % 3 === 0) {
return {
invalidState: true
}
} else if (random % 3 === 1) {
return {
invalidState: false
}
} else {
throw Error("Error from function");
}
}
doSomeOperation()
.then((result) => console.log("Success"))
.catch((error) => console.log("Failed", error.message))
You can simply use throw instead of using Error constructor
const doSomeOperation = async () => {
try {
throw {customError:"just throw only "}
} catch (error) {
console.log(error)
}
};
doSomeOperation()

Categories