Reputation: 2387
I'm new to Javascript Promises. I'm trying to perform a MySQL query in Promises based style I've been scratching my head for over half an hour but I still can't find the problem in this code. I'm using this article as a reference.
mysql file
const mysql = require('mysql')
const Config = require('./Config.js');
let connection = mysql.createPool({
connectionLimit: Config.mysql.connectionLimit,
host: Config.mysql.host,
user: Config.mysql.user,
password: Config.mysql.password,
database: Config.mysql.database,
multipleStatements: true,
});
module.exports = {
query: function(query, binds) {
return new Promise((resolve, reject)=>{
connection.query(query, binds, (error, ret)=>{
if(error)
return reject(error);
else
resolve(ret);
})
})
},
close: function() {
return new Promise((resolve, reject)=>{
connection.end((error)=>{
if(error)
return reject(error);
else
resolve();
});
});
}
}
actual script
const Mysql = require('mysql file');
try {
Mysql.query("SELECT `password` \
FROM `m_users` \
WHERE `email` = ?",
['[email protected]'])
.then(ret=>{
if(ret.email)
return res.send({
params: req.body,
message: 'Email address has already been taken.',
})
})
.catch(error=>{
throw error;
})
catch( e )
{
console.log(e); // is never reached
}
throw error
is never fired. Instead in my node console I receive the following error:
(node:8684) UnhandledPromiseRejectionWarning: Error: ER_NO_SUCH_TABLE: Table 'forge.m_users' doesn't exist
........backtrace here .......
(node:8684) 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)
I know the table does not exist. I'm interested in finding out why .catch
does not handle the error. What am I doing wrong? :/
Upvotes: 0
Views: 742
Reputation: 1074286
This is an anti-pattern (but you're not by far the first person to do it!):
.catch(error=>{
throw error;
})
What that says is "Create a new promise" (because catch
creates a promise) "and if the original promise rejects, reject it with the exact same rejection reason (error
)." So it's basically a no-op, it doesn't do anything. It's the promise equivalent of:
try {
// something
} catch (error) {
throw error;
}
Since it doesn't do anything, consider what the code looks like without it — the code is breaking one of the fundamental rules of promises, which is "handle rejections or pass the chain to something else that will." :-)
If you want to handle the rejection, don't throw
from catch
:
Mysql.query("SELECT `password` \
FROM `m_users` \
WHERE `email` = ?",
['[email protected]'])
.then(ret=>{
if(ret.email) {
return res.send({
params: req.body,
message: 'Email address has already been taken.',
});
}
})
.catch(error=>{
// Use `error` here without `throw`ing -- report it, put it in a log, etc.
});
In an update to the question you show that you have all of your code using the promises in a try
/catch
:
try {
Mysql.query(/*...*/)
.then(/*...*/)
.catch(error=>{
throw error;
});
}
catch( e )
{
console.log(e); // is never reached
}
In a non-async
function, try
/catch
can't catch promise rejections, because by the time the promise is rejected, execution has already left the try
block. When using promises directly, you use the catch
method instead. Or you use async
functions (more below).
Side note: Node.js has had support for async
functions for a while now, which tend to be a bit simpler to write. Within an async
function, your code could be:
// Within an `async` function
try {
const ret = await Mysql.query("SELECT `password` \
FROM `m_users` \
WHERE `email` = ?",
['[email protected]']);
if(ret.email) {
return res.send({
params: req.body,
message: 'Email address has already been taken.',
});
}
} catch (error) {
// Use `error` here without `throw`ing -- report it, put it in a log, etc.
}
Using async
at the top level of a module is coming soon, too...
In a comment you've asked:
I'm building an MVC API and I run more promise-based procedures in the same controller method. I'd like to return a message once an exception has been encountered and stop continuing the execution of the next other promised-based procedures. I'm bound to using async/await method or is there another way?
You don't have to use async
/await
if you don't want to, no; they just make things simpler (in my view).
If you don't want to use async
/await
, in the fulfillment callback you're passing then
, return the promise from the next operation. That makes the promise then
fulfill or reject based on what the promise frm the next operation does — in promise jargon, it resolves the promise from then
to the one from the next operation.
Code is worth 1024 words, so:
doSomethingAsync()
.then(value => {
// ...use `value`, then
return doSomethingElseAsync();
})
.then(() => {
// ...
return doYetAnotherAsyncThing();
})
.catch(error => {
// Handle/report error here
});
If any of the promises is rejected, any subsequent fulfillment handlers are skipped, and the rejection handler at the end (added via catch
) is triggered.
That's roughly the promise version of this synchronous code:
try {
const value = doSomethingSync();
// ...use `value`, then
doSomethingElseSync();
// ...
doYetAnotherSyncThing();
} catch (error) {
// Handle/report error here
}
Upvotes: 2
Reputation: 976
basically, the reason you're getting an unhandled rejection is mixing async and sync code - promises are asynchronous and try{ ... } catch() { ... }
only work in the synchronous mode. calling an asynchronous function in a synchronous try-catch block does not wait for it to settle. try to wrap your promise with async/await
or handle the rejection in .catch()
of the promise itself.
Upvotes: 1