throwing UnhandledPromiseRejection even the code wrapped in try catch - javascript

I am getting UnhandledPromiseRejection error even I wrapped the code in try catch block
I using await Prmomise.all together here
const express = require('express');
const app = express();
const port = 3003;
function testPromise(n) {
return new Promise(async (res, rej) => {
console.log(n);
if (n > 10) {
res(true);
} else {
setTimeout(() => {
rej(n);;
}, 1000)
}
});
}
function test2(n) {
return new Promise(async (res, rej) => {
console.log(n);
if (n > 10) {
res(true);
} else {
setTimeout(() => {
rej(n);;
}, 10000)
}
});
}
async function allCall(p) {
await Promise.all(p);
}
app.get('/', async (req, res) => {
try {
let a = [];
let b = [];
a.push(testPromise(1));
await test2(1);
a.push(testPromise(12));
// await Promise.all(a.map(m => m.then(() => { }).catch(err => { })));
await Promise.all(a);
res.send('Hello World!');
} catch (err) {
console.log('err');
console.log(err);
res.status(400).send('xxxxxxxxxx!')
}
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
I am not sure why it is throwing the error
[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().
Please explain why and how to resolve this ?

You are getting this error because 2 promises are getting rejected but try/catch only handles one. The second promise is rejected but not handled.
1st rejection: a.push(testPromise(1));
2nd rejection: await test2(1);
NOTE:
Both the promises are started parallel.
try/catch only works with async/await, if you write promise inside try/catch it'll not be handled by the catch block.
try {
Promise.reject("Something")
} catch (error) {
console.log('Not here');
}
// Unhandled promise rejection
Explanation:
When you push a promise to an array a.push(testPromise(1));, it starts execution and rejects after 1 second. It goes to catch.
same time the second promise also started await test2(1); because you are not waiting for the first promise to resolve/reject. It'll get rejected after 1 second and not handled by the catch block. it'll go to catch only if you use with await. If you want to handle first rejection you have to use .catch. after 10 seconds second promise get rejected and handled by catch block.
Solution
const r = await testPromise(1);
await test2(1);
Another solution:
async (req, res) => {
try {
// a.push(testPromise(1));
await test2(1);
let a = [testPromise(1), testPromise(12)];
await Promise.all(a);
console.log("done");
} catch (err) {
console.log("err");
console.log(err);
}
};

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');
})();

Try/Catch in mulitple awaits in express app

I am making a few database calls and I am using async/await and try/catch for error handling. I am struggling if I should have all of the db calls in one try/catch, or have multiple try/catch` blocks for each call.
I also have a few calls in callback fncs, I am not confident those calls will be caught in my catch block if I only have one try/catch. With that in mind, those calls have their own try catch block. Here is a working example:
exports.syncStaff = async function (req, res, next) {
// ShiftTask && Shift is a model from mongoose
try {
// DB CALL #1 --> Inside of Try/Catch Block
const shift = await Shift.findById(req.params.id);
// DB CALL #2 + 3 --> Two calls run in parallel --> Inside of Try/Catch Block
const [shiftTasks, shiftType] = await Promise.all([
ShiftTask.find({ group: shift.id }),
mongoose.model('ShiftType').findById(shift.type).populate('tasks').select('tasks')
]);
await Promise.all(shiftTasks.filter(st => !shiftType.workshops.find(type => type.id.toString() === st.task.toString() || st.status !== 'pending')).map(task => {
// DB CALL #4 --> Separate Try/Catch Block, is this needed?
try {
return ShiftTask.remove({ _id: task.id });
} catch (error) {
console.error(error);
next(error);
}
}));
await Promise.all(shiftType.workshops.filter(type => !shiftTasks.find(task => task.shift.toString() === type.id.toString())).map(type => {
try {
// DB CALL #5 -- Separate Try/Catch Block, is this needed?
return ShiftTask.create({
group: shift.id,
eventType: type.id
});
} catch (error) {
console.error(error);
next(error);
}
}));
return await res.status(201).json('still to be decided');
} catch (error) {
console.error(error);
next(error);
}
};
Are the try/catch blocks in db calls #4 and #5 necessary?
I don't think external try catch blocks are needed. If an error is thrown from somewhere, it can be caught from the block in the public container. I made an example like this. You can test the 2nd case specified in the code from the google chrome console.
let compPromise = new Promise(function(resolve, reject) {
resolve('Complete');
});
let errPromise = new Promise(function(resolve, reject) {
reject(new Error("Whoops promise reject!"))
});
let exec = async () => {
try {
let res1 = await compPromise;
console.log('res1', res1);
let [res2,res3] = await Promise.all([
compPromise,
compPromise
])
console.log('res2', res2);
console.log('res3', res3);
// In this case, reject will return from the promise and will catch it in the catch block.
await Promise.all([10, 20, 30].map((x) => x === 30 ? errPromise : compPromise))
// In this case, the parameter was deliberately sent as undefined and will still be caught in the catch block.
await Promise.all([undefined, 'Johnny', 'Alison'].map((x) => x.trim().includes("n") ? errPromise : compPromise))
} catch(err) {
console.log(err.toString());
}
}
exec()

async in map with callback won't work file be still undefined

hello all I have some problems with my async function the test will be undefined what am I doing wrong I need help with this it's so frustrating
async function fileToObj(jsonOfXls){
const promises = jsonOfXls.Blad1.map(async x => {
let test;
await base64.encode(`pdfs/${x.E}`, function (err, base64String) {
test = base64String
})
return { gtin: x.D, gln: x.C, order: x.B, file: test }
})
const output = await Promise.all(promises)
console.log(output)
}
i try now this :
async function fileToObj(jsonOfXls) {
const output = await Promise.all(
jsonOfXls.Blad1.map(async x => {
const file = await new Promise((resolve, reject) => {
base64.encode(`pdfs/${x.E}`, function(err, base64String) {
if (err != null) {
return reject(err)
}
resolve(base64String)
})
})
return { gtin: x.D, gln: x.C, order: x.B, file }
})
)
console.log(output)
}
but i get this error:
72) UnhandledPromiseRejectionWarning: encode fail
(node:8772) 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(). (reject
ion id: 1)
You can only usefully await a promise.
base64.encode takes a callback which implies it doesn't return a promise.
Awaiting its return value therefore has no practical effect.
You would need to wrap it in a promise before you can await it.

Promise not resolving after connection completes

I have a mongoose connect function in which I try to wait for reconnecting if the first attempt fails:
async connect() {
const options = {...}
try {
console.log("starting")
await this._connectWithRetry(options)
console.log("finished")
} catch (err) {
winston.error(`Could not connect to Mongo with error: ${err}`)
}
}
private async _connectWithRetry(options) {
return new Promise( async (resolve, reject) => {
try {
winston.info("Connecting to mongo...")
await mongoose.connect(this.dbURI, options)
winston.info("Connection successful.")
resolve()
} catch (err) {
winston.info("Failed to connect to mongo. Retrying in 5 seconds...")
setTimeout( async () => {
await this._connectWithRetry(options)
}, 5000)
}
})
}
It successfully waits until I'm connected. But once I connect, the second console line is not hit ("finished"). so I figure that my promise resolution is buggy. What am I doing wrong?
Your code "works" if the connection to the DB is established first time around.
If the retry mechanism is used, you will see the error you describe.
The Promise instantiated by the first call to mongoDBConnect is never resolved in the retry execution path.
This is because subsequent invocations of mongoDBConnect are made in a totally separate execution context on a future tick of the event loop, controlled by the setTimeout - and each invocation instantiates a new Promise totally disconnected from your connect function.
This refactoring should fix the issue:
const delay = (interval) => new Promise(resolve => setTimeout(resolve, interval))
async connect() {
const options = {...}
try {
console.log("starting")
await this._connectWithRetry(options)
console.log("finished")
} catch (err) {
winston.error(`Could not connect to Mongo with error: ${err}`)
}
}
private async _connectWithRetry(options) {
try {
winston.info("Connecting to mongo...")
await mongoose.connect(this.dbURI, options)
winston.info("Connection successful.")
} catch (err) {
winston.info("Failed to connect to mongo. Retrying in 5 seconds...")
await delay(5000)
await this._connectWithRetry(options)
}
}
Test harness:
let retryCount = 0
const mongoose = {
connect: ()=>retryCount++ === 2 ? Promise.resolve() : Promise.reject('fake error')
}
async function connect() {
try {
console.log("starting")
await connectWithRetry()
console.log("finished")
} catch (err) {
console.error(`connect error`, err)
}
}
async function connectWithRetry() {
try {
console.log("Connecting to mongo...")
await mongoose.connect()
console.log("Connection successful.")
} catch (err) {
console.log("Retrying in 1 second...", err)
await delay(1000)
await connectWithRetry()
}
}
const delay = (interval) => new Promise(resolve => setTimeout(resolve, interval))
connect()

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.

Categories