NodeJS: Unhandled promise rejection - javascript

I'm having a little problem and after debugged all the app I noticed that this is the file that's causing the problem, returning me a UnhandledPromiseRejection
'use strict'
const connection = require('../models/'),
oracledb = require('oracledb'),
conexion = oracledb.getConnection(connection)
oracledb.outFormat = oracledb.OBJECT;
module.exports = {
index(req, res) {
conexion.then(con => {
return con.execute(
`SELECT id_application, name, description, creation_date ` +
`FROM application `
).then(bucket => {
return con.execute(
`SELECT id_definition, id_application, field_name_original, field_name_new,
column_name, position, id_type_data, field_size, creation_date,
description, filter, visible ` +
`FROM definition `
).then(definitions => {
res.status(200).json(creaJSON(bucket, definitions))
}).catch(error => { return res.status(500).json({'message': error}) })
}).catch(err => { return res.status(500).json({'message': err}) })
}).catch(err => { return res.status(500).json({'message': err}) })
},
create(req, res) {
},
update(req, res) {
}
}
const doRelease = (connection) => {
connection.close((err) => {
if(err) console.error(err.message);
})
}
const creaJSON = (buckets, definitions) => {
var df = new Array()
buckets['rows'].map(obj => {
definitions['rows'].map(def => {
if(obj['ID_APPLICATION'] == def['ID_APPLICATION']) df.push(def)
})
obj['Definitions'] = df
df = []
})
return buckets.rows
}
after the UnhandledPromiseRejection is being followed by: Error: ORA-12170: TNS:Connect timeout occurred
(node:1270) 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.
I already looked for solutions, some says that promises are not catching correctly but I don't see any problem with them. Any other suggestion?
Any help will be welcome.
Thanks

const connection = require('../models/'),
oracledb = require('oracledb'),
conexion = oracledb.getConnection(connection)
is setting conexion to the promise returned by a call to .getConnection made when the entire source file is executed (in response to being required).
conexion has no handlers at this point. Handlers are only added later when the indexmethod of the exported {index, create, update} object is called.
Hence connection timeout in between the source file being required and index being called will produce an unhandled rejection error.
Obviously adding a catch clause such as
conexion = oracledb.getConnection(connection).catch( onRejected)
should fix this error, but how much recovery you want to put into coding onRejected is up to you.
Edit:
A less obvious approach to satisfying V8's version of how to handle uncaught promise rejection is to provide a dummy handler to thwart it:
conexion = oracledb.getConnection(connection);
conexion.catch(()=>undefined); // a do nothing catch handler.
Here the second line adds a handler to the conexion promise, making it "handled", which prevents it ever becoming an uncaught promise rejection. The promise returned by catch is superfluous and not recorded, but will be fulfilled if the no-operation catch handler is ever called.
Now the promise held in conexion can be rejected before index is called without generating an exception. Whether this is the best way to code promise topology for a particular application is a different question - you may very well wish to address a connection timeout earlier.

Related

How to avoid ` PromiseRejectionHandledWarning: Promise rejection was handled asynchronously`?

I'm getting PromiseRejectionHandledWarning: Promise rejection was handled asynchronously warning for my code, it also fails Jest tests. I've read that's Promise rejection should be handled at the same place they're defined and seem to understand the logic and the reasons. But it looks like something is wrong with my understanding as what I expect to be synchronous handling still cause warning.
My typescript code is below.
Promise, that is rejected is sendNotification(subscription, JSON.stringify(message)). From my understanding it's handled right away using .catch call, but probably I'm missing something. Can anyone, please, point me to my mistake?
private notify(tokens: string[], message: IIterableObject): Promise<any> {
const promises = [];
tokens.forEach(token => {
const subscription = JSON.parse(token);
this.logger.log('Sending notification to subscription', {subscription, message})
const result = this.WebPushClient
.sendNotification(subscription, JSON.stringify(message))
.catch(e => {
this.logger.log('Send notification failed, revoking token', {
subscription,
message,
token,
e
})
return this.revokeToken(token).catch(error => {
this.logger.error('Failed to revoke token', {
token,
error,
})
return Promise.resolve();
});
});
promises.push(result);
});
return Promise.all(promises);
}
I found the issue.
In Jest you can't just mock return value with rejected promise. You need to wrap it in special workaround function:
const safeReject = p => {
p.catch(ignore=>ignore);
return p;
};
And then wrap the Promise before return it
const sendNotification = jest.fn();
sendNotification.mockReturnValue(safeReject(Promise.reject(Error('test error'))));
You can also use mockImplementation (I'm using jest 28).
jest.fn().mockImplementation(() => Promise.reject(new Error('test')))

Intentionally not returning Bluebird Promise

I have the piece of code below. Want to call the callback which may return a promise. Resolve it. In case if the promise failed, log it. The caller should NOT know about all this and should return without waiting for the promise to fulfill. That's why I'm not returning the promise. This causes the following error:
(node:21146) Warning: a promise was created in a handler at internal/timers.js:456:21 but was not returned from it, see http goo.gl/rRqMUw
at Function.Promise.cast (bluebird/js/release/promise.js:225:13)
I've read the docs and they recommend returning null to prevent the warning from happening. Nevertheless, the warning still pops out. Also, do not want to disable the warning globally.
private _trigger(cb : () => Resolvable<any>)
{
try
{
const res = cb();
const prom = Promise.resolve(res);
prom.catch(reason => {
this.logger.error("ERROR: ", reason);
})
}
catch(reason)
{
this.logger.error("ERROR: ", reason);
}
return null;
}
The internal Promise should resolve to a value of null to silence the warning - this will tell Bluebird "The Promise isn't being returned, but since it resolves to null, it doesn't contain a useful result, so this is deliberate"; returning it wouldn't give the caller useful data. You can do something like:
const prom = Promise.resolve(res)
.catch(reason => {
this.logger.error("ERROR: ", reason);
})
.then(() => null);

Async/await call returns undefined when used in conjunction with promises

I am having an issue where an Async call to my database returns undefined.
The function "findOne" retrieves one row from the database, but the .then(... function is executing before the row is returned.
I've tried changing what I return in the DB function findOne as well as adding an 'await' on the function call. I've also tried using Promise.resolve(db.findOne({requestbody}).then(... but no luck with that either.
Here is the db.findOne method
const findOne = async (req) => {
const { teamId, channelId, isClosed } = req;
return db.query('SELECT * FROM polls where team_id= $1 and channel_id =$2 and is_closed = $3 LIMIT 1',
[teamId, channelId, isClosed],
(error, results) => {
if (error) {
throw error;
}
console.log("\nDBRes: \n", results.rows[0])
return results.rows[0];
}
);
};
And here is where I call the function
app.post('/', (req, res) => {
const slashCommand = req.body.command;
switch (slashCommand) {
//...
//... Some other code
//...
case 'results':
db.findOne({
teamId: requestBody.team_id,
channelId: requestBody.channel_id,
isClosed: false,
})
.then((row) => {
console.log(row);
const poll = pollFuncs.getPollfromResultRow(row);
const displayText = pollFuncs.getFormattedPollResults(poll);
res.status(200).send({
text: displayText,
});
});
break;
//... The rest of the function
Here are the logs I am getting.
Note* I am currently logging the "row" object both inside the .then(...) function and inside the pollFuncs.getPollfromResultRow(row); function
Bot is listening on port 3000
undefined
undefined
(node:14000) UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property `id` of 'undefined' or 'null'.
at Object.getPollfromResultRow (C:\Users\ztb0504\Documents\Projects\Node\werewolfmod\pollFunctions.js:97:125)
at db.findOne.then (C:\Users\ztb0504\Documents\Projects\Node\werewolfmod\index.js:59:56)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:14000) 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: 1)
(node:14000) [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.
DBRes:
{ id: '22',
poll_title: 'This is a new Pollstgresql',
//The rest of the expected data....
}
I'd appreciate any guidance on how to get this to return data as expected.
Thank you!
You're mixing plain callbacks and promises and it is causing you problems. It will be a lot easier if you don't do that.
If you pass a plain callback to db.query(), then it won't return a promise. In fact, it will return nothing (undefined). So, when you do return db.query(), all you're doing is returning undefined.
Change to this:
const findOne = async (req) => {
const { teamId, channelId, isClosed } = req;
return db.query('SELECT * FROM polls where team_id= $1 and channel_id =$2 and is_closed = $3 LIMIT 1',
[teamId, channelId, isClosed]).then(results) => {
console.log("\nDBRes: \n", results.rows[0])
return results.rows[0];
});
};
The, you also need error handling in your request handler if there are any errors in the query. Promise handling should nearly always have a .catch() somewhere to handle errors:
case 'results':
db.findOne({
teamId: requestBody.team_id,
channelId: requestBody.channel_id,
isClosed: false,
}).then((row) => {
console.log(row);
const poll = pollFuncs.getPollfromResultRow(row);
const displayText = pollFuncs.getFormattedPollResults(poll);
res.status(200).send({
text: displayText,
});
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
break;

Error "Unhandled promise rejection" with mysql query

I am trying to make a new data entry in BD with mysql from a page. But when throw me an error about promises
var mysql = require('mysql'); //Llamadi a MySql
var valida= require('./modulos/validaciones');
var conection = mysql.createConnection({
host: 'localhost', //Host
user: 'eleazarsb', //Usuario
password: 'eleazar616', //ContraseƱa
database: 'mydb' //Base de datos
}).connect(function(error) {
if (error) console.log('Problemas de conexion con mysql');
console.log("conexion Exitosa!");
});
var validaciones = {
user: false,
mail: false,
ced: false
}
//Pendiente: 02
valida.user(data).then((rows) => {
validaciones.user = rows;
})
valida.mail(data).then((rows) => {
validaciones.mail = rows;
})
valida.ced(data).then((rows) => {
validaciones.ced = rows;
registrar(validaciones);
})
function registrar(validaciones) {
if (validaciones.user == false && validaciones.mail == false && validaciones.ced == false) {
var query = conection.query(data.sqlquery(), (error, columna, campos) => {
if (error) throw error;
console.log("Entra en registro de BD");
console.log(columna);
});
}
else {
console.log("No se registro nada");
};
return query;
};
When 'conection.query(data.sqlquery()...' is execute the console throw me this error
(node:1588) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'query' of undefined
(node:1588) [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.
So, i cant see where is the error if conection.query( is declared in the beginning with conection=mysql.createConnection
I am new programming with NodeJs so i i accept any comment or suggestion of good programming practices
valida return a promise declared in another file
exports.mail= (data)=>{
return new Promise ((resolve,reject)=>{
var query=conection.query(data.valida_mail(),(error,rows)=>{
if (error) return reject (error);
if(rows[0]){
console.log("Email Invalido!");
resolve(true);
} else {
console.log("Email Valido");
resolve(false);
}
});
})
};
... for example
Will's explained why your code is failing. The reason you're seeing it as an "Unhandled rejection" error is that you're not handling your promises correctly.
One of the rules of promises is that you either pass on the promise chain to the level above you, or you handle errors. This code does neither (nor do others written largely the same way):
valida.user(data).then((rows) => {
validaciones.user = rows;
})
What if the promise returned by valida.user(data) rejects? Answer: Nothing handles it.
You must either hand off the promise then creates to something else, or handle rejections. (If you hand it off, the thing you had it off to has to hand it off again, or handle errors.)
To make that code handle errors, add a catch handler:
valida.user(data).then((rows) => {
validaciones.user = rows;
}).catch(err => {
// Do something with the error here
});
The error message is a bit misleading here. The important part is TypeError: Cannot read property 'query' of undefined which is referring to conection being undefined when it is called in registrar when that is called in the promise handler attached to valida.ced.
The issue is with the code:
var conection = mysql.createConnection({
...
}).connect(function(error) {
...
});
which is assigning the return value of the call to Connection.connect to your conection variable. Connection.connection does not return a value (source), so later when that promise resolves and tries to execute registrar(), conection is still and forever undefined, so there's no such thing as conection.query to be called. Try:
var conection = mysql.createConnection({
...
});
conection.connect(function(error) {
...
});
As T.J. points out, the other important part of the error message is that you should provide code to handle rejected promises. Use the catch function to attach these handlers.
Adding to the answers .
You are using node.js promises . So to handle all type of 'Unhandled promise rejection'. use this code at the main node.js app file
process.on('unhandledRejection', error => {
// Will print "unhandledRejection err is not defined"
console.log('unhandledRejection', error);
});
In case you missed catching some promise this will handle all of them.
Process
Node.js process is global , it contains unhandledRejection event for handling unhandled promise rejection .

Using promises with Node JS express

I am newbie to promises, I am trying to understand how do they work.
Here my questions at first:
When request is handled in a route function, does it wait for all promises, I mean that when I am using promise or callback it is a new scope and execution continues further.
If I keep a req/res objects for example in timer and then respond to user, what will user see ? A request will just be blocked until I explicitly send response ?
So I have encountered the following problems.
Here is my route.
router.post('book', authHandler.provideMiddleware(), (req, res) => {
bookManager.createBook(req.body, {
onSuccess: function (data) {
respondSuccess(res,HttpStatus.OK, {'data': data});
},
onError: function (message) {
respondError(res, HttpStatus.HTTP_STATUS.BAD_REQUEST, {'error': message});
}
});
});
Inside bookmanager I have the following
createBook(data, hook) {
let book = createBookFromRequest(data);
let verifyData = new Promise((resolve, reject) => {
let valid = checkBookData(book);
if(valid) {
resolve(book);
}
else {
reject("Invalid data");
}
});
let createBook = new Promise((resolve, reject) => {
book.save((err, result) => {
if (!err) {
reject("Error while saving");
}
else {
resolve(result);
}
});
});
verifyData
.then(() => {
return createBook;
})
.then((data) => {
hook.onSuccess(data);
})
.catch((error) => {
hook.onError(error);
});
}
My idea is to chain multiple functions and if any error occurred, call hook.onError method, otherwise call on success.
I have several problems here.
When error is thrown, my book is still created.
I have the following error
node:8753) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 6): Error: Can't set headers after they are sent.
I want to use the approach like Rx (Reactive Extensions).
Could someone explain what is wrong and how do promises really work in this case ?
1. When request is handled in a route function, does it wait for all promises, I mean that when I am using promise or callback it is a new scope and execution continues further.
It waits for you to send a response via res. You don't have to do that in response to the event, it's absolutely normal for it to be later, after an asynchronous process (like a promise resolution) completes.
2. If I keep a req/res objects for example in timer and then respond to user, what will user see ? A request will just be blocked until I explicitly send response ?
Yes.
I have several problems here.
1. When error is thrown, my book is still created.
You're always starting the process of creating the book, regardless of whether the data was verified as correct. new Promise starts the work.
2. I have the following error
node:8753) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 6): Error: Can't set headers after they are sent.
You're creating a promise and storing it in createBook, and never handling rejections of that promise if verifyData rejects. So you get an unhandled promise rejection.
You can get rid of the entire new Promise around saving the book, and just put it in the verifyData chain; see comments:
createBook(data, hook) {
let book = createBookFromRequest(data);
let verifyData = new Promise((resolve, reject) => {
let valid = checkBookData(book);
if (valid) {
resolve(book);
}
else {
reject("Invalid data");
}
});
verifyData
.then(() => book.save()) // The chain takes on the state of the promise
// returned by `book.save`
.then(data => {
hook.onSuccess(data);
})
.catch((error) => {
hook.onError(error);
});
}
In that, I'm assuming createBookFromRequest and checkBookData are both synchronous processes, as that's how you're using them.
And actually, given that's the case, I don't see any need for the promise you're creating to verify the data. So it could be simpler:
createBook(data, hook) {
let book = createBookFromRequest(data);
if (checkBookData(book)) {
book.save()
.then(_ => hook.onSuccess(data))
.catch(error => hook.onError(error));
} else {
hook.onError("Invalid data");
}
}

Categories

Resources