What is the best way to handle this scenario. I am in a controlled environment and I don't want to crash.
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
When throwing from within the setTimeout we will always get:
$ node bluebird.js
c:\blp\rplus\bbcode\scratchboard\bluebird.js:6
throw new Error("AJAJAJA");
^
Error: AJAJAJA
at null._onTimeout (c:\blp\rplus\bbcode\scratchboard\bluebird.js:6:23)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
If the throw occurs before the setTimeout then bluebirds catch will pick it up:
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
throw new Error("Oh no!");
setTimeout(function(){
console.log("hihihihi")
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
Results in:
$ node bluebird.js
Error [Error: Oh no!]
Which is great - but how would one handle a rogue async callback of this nature in node or the browser.
Promises are not domains, they will not catch exceptions from asynchronous callbacks. You just can't do that.
Promises do however catch exceptions that are thrown from within a then / catch / Promise constructor callback. So use
function getPromise(){
return new Promise(function(done, reject){
setTimeout(done, 500);
}).then(function() {
console.log("hihihihi");
throw new Error("Oh no!");
});
}
(or just Promise.delay) to get the desired behaviour. Never throw in custom (non-promise) async callbacks, always reject the surrounding promise. Use try-catch if it really needs to be.
After dealing with the same scenario and needs you are describing, i've discovered zone.js , an amazing javascript library , used in multiple frameworks (Angular is one of them), that allows us to handle those scenarios in a very elegant way.
A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for JavaScript VMs
Using your example code :
import 'zone.js'
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
Zone.current
.fork({
name: 'your-zone-name',
onHandleError: function(parent, current, target, error) {
// handle the error
console.log(error.message) // --> 'AJAJAJA'
// and return false to prevent it to be re-thrown
return false
}
})
.runGuarded(async () => {
await getPromise()
})
Thank #Bergi. Now i know promise does not catch error in async callback. Here is my 3 examples i have tested.
Note: After call reject, function will continue running.
Example 1: reject, then throw error in promise constructor callback
Example 2: reject, then throw error in setTimeout async callback
Example 3: reject, then return in setTimeout async callback to avoid crashing
// Caught
// only error 1 is sent
// error 2 is reached but not send reject again
new Promise((resolve, reject) => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Nothing happen
})
.then(() => {})
.catch(err => {
console.log("Error", err);
});
// Uncaught
// error due to throw new Error() in setTimeout async callback
// solution: return after reject
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Did run and cause Uncaught error
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});
// Caught
// Only error 1 is sent
// error 2 cannot be reached but can cause potential uncaught error if err = null
new Promise((resolve, reject) => {
setTimeout(() => {
const err = "error 1";
if (err) {
reject(err); // Send reject
console.log("Continue"); // Did print
return;
}
throw new Error("error 2"); // Potential Uncaught error if err = null
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});
Related
Im adding a duplicate to a mysql table and I want to handle elicited ER_DUP_ENTRY error comming back with a Try/Catch block but its just crashing anyway , is there any possible way to handle error and stop application from crashing using a try/catch block?
async function init() {
try {
connection.query(
'SOME INSERT QUERY',
(err, result, feilds) => {
if (err) throw err
console.log(result);
}
);
} catch (e) {
console.log(e);
}
}
init();
The node mysql-library does not support promises out of the box, which means query does not return a promise which you can await. So you can either wrap the query function in a promise yourself:
async function init() {
try {
const duplicateResult = await new Promise((resolve, reject) => {
connection.query(
'SOME INSERT QUERY',
(err, result, fields) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
} catch (e) {
console.log(e);
}
}
or use util.promisify as Always Learning posted alternatively.
The problem is that connection.query returns undefined right away. Your catch is not involved because the call ends before the work is done and will call your callback function later. An exception that occurs during your callback is too late. You try/catch block has already completed.
You can use promisify to wait on it like this though:
const util = require("util");
function init() {
const queryPromise = util.promisify(connection.query);
return queryPromise('SOME INSERT QUERY')
.catch(e => {
console.log("It failed", e);
});
}
init().then(result => {
if (result) console.log("It worked", result);
else console.log("Aww, it didn't work");
});
I'm using node.js and express, also mysql.
I use a connection pool to request connections and create a promise on it, to limit callback nightmare, the following snippet is set in a file that I import later, note that that I set an handler on error to not terminate the application in case of anything going really wrong
exports.getConnection = () => {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) {
reject(`Could not obtain the connection from the pool: ${err}`);
}
connection.on('error', err => {
console.log(`SQL error (code: ${err.code}, message: ${err.sqlMessage}) while executing query: ${err.sql}`);
});
resolve(connection);
});
});
};
And here is an example of usecase (the idea is to get the connection, chain the query in the then, and if a non fatal error happen I will throw it and handle the connection release in the catch handler
// Exception handler that release the connection then call the callback
function releaseConnectionHandler(err, connection, callback) {
connection.release();
callback(err, null);
}
exports.someRequest = function(ID, callback) {
sqlPool.getConnection().then(connection => {
connection.query("SELECT * from tableNotExists",
(err, result) => {
if (err) {
throw ({ err, connection, callback });
}
connection.release();
callback(null, result);
});
}).catch(({ err, connection, callback}) => releaseConnectionHandler(err, connection, callback));
};
The query will fail, but I see that the handler is not even called (I put some trace in it...) and the application terminates on
node_modules/mysql/lib/protocol/Parser.js:80
throw err; // Rethrow non-MySQL errors
Correct querie yoeld no troubles...Any ideas what I did wrong on the error handling ?
You're re-throwing the error passed to the callback of your query, which the library you're using then re-throws as well, and finally isn't properly caught and handled anywhere and results in a failure. You're not in the context of the Promise when you throw, but the context of the callback function called from the mysql module.
You're also unnecessarily mixing promises and callbacks, in particular the function you're exporting. Your question indicates that you want to move away from callbacks, so I'm going to base this answer on that indication.
To solve the main issue, don't throw the error. Instead, pass it up to the callee:
const promisify = require("util").promisify;
exports.someRequest = function (ID) {
return sqlPool.getConnection().then(connection => {
return promisify(connection.query)("select * from tableNotExist")
.finally(connection.release);
});
};
The connection will always be released back to the pool, whether successful or on error. You can then call the method with:
yourModule.someRequest(id).then((results) => {
// Do something with the result set
}).catch((e) => {
// Handle error. Can be either a pool connection error or a query error.
});
If you have the possibility to use async/await, the code can be rewritten:
const promisify = require("util").promisify;
exports.someRequest = async function (ID) {
let connection = await sqlPool.getConnection();
try {
return await promisify(connection.query)("select * from tableNotExist");
} finally {
connection.release();
}
};
I also recommend using node-mysql2 since they have a Promise-based API in addition to their callback-style API and in my experience better performance as well. Then you don't have to write these tedious wrappers and instead just require('mysql2/promise') and be good to go.
I'm getting
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 44): Error: fail
main.js
import { request } from './api'
async getData({ commit, state }, ids ){
try {
var x = await request(ids)
commit('getData', x.data)
} catch ( e ) {
console.log('request failed get',e.code,e.errno)
}
}
api.js
export async function request(type,url,ids){
axios.get('localhost/data')
.then(function (response) {
return Promise.resolve(response.data)
})
.catch(function (e) {
return Promise.reject(new Error('fail'))
})
}
How can I handle the promise rejection? Shouldn't the try catch block catch the error from the await function here?
You are mixing up async/await with promises. In api.js, there is no need to use the async keyword. The async keyword makes it so that anything you return from the function is wrapped in a promise, which you don't need, since axios.get returns a promise already.
Also, you forgot to actually return the promise from Axios, your request function just returns undefined.
Lastly, you don't have to return promises from the then and catch methods, just return a value, or throw an error.
If you rewrite the function like this it should work as expected:
export function request(type,url,ids){
return axios.get('localhost/data')
.then(function (response) {
return response.data
})
.catch(function (e) {
throw new Error('fail')
})
}
var promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
});
we can use "try-catch" to throw error
// mothod 2
var promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
we also can use "reject" to throw error.
what's difference between them?
Exceptions thrown from asynchronous functions cannot be handled with try-catch block.
Promises are chain-able; prevents us from nesting code that affects readability.
There is no effective difference. In both cases, you're calling reject() with an error.
The following are also equivalent to what you have there:
var promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
var promise = Promise.reject(new Error('test'));
var promise = Promise.resolve().then(function () { throw new Error('test'); });
I'm making a simple database call wrapped in a promise, and trying to catch the error and pass it through to the promise reject(), but the reject isn't being handled or bubbled back up by the calling code. The code just stops executing when the mysql call fails.
The error within the mysql callback is:
REJECTING QUERY { Error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '?' at line 1
Here is the database query code:
this.query = function(sql, params) {
return new Promise((resolve, reject) => {
_pool.query(sql, params, function(err, result) {
if (err) {
console.log("REJECTING QUERY", err);
return reject(err);
}
resolve(result);
});
});
}
Here is the calling code:
this.createUser = function(data) {
var query = "INSERT into users SET ?";
return new Promise((resolve, reject) => {
Container.DB.query(query, data)
.then((response) => {
console.log("Resolved", response);
return resolve(response);
},(error) => {
console.log("REJECTION ERROR", error);
return reject('An unknown error occurred and has been reported.');
})
.catch((err) => {
console.log("CAUGHT ERROR", err);
});
});
}
I get to "REJECTING QUERY" within the database query code, but nothing in the calling code is reached (ie. .then, or the error handler, or the .catch handler).
Is it possible to get the mysql error to reach these points in code so I can respond to the user? Am I doing something wrong?
The anti-pattern mentioned by #JaromandaX is forcing you to unnecessarily jump through flaming hoops to accommodate it... and your getting burned.
But, first, you are rejecting to the outer (redundant) promise from the then before the catch so the catch is by-passed. After an error is thrown in a promise chain, the first thenable with a second argument (onRejected) will consume it: so it won't be propagated beyond that. But, anyway, you need to trap the error on the outer promise which you are rejecting.
this.createUser = function (data) {
var query = "INSERT into users SET ?";
return new Promise((resolve, reject) => { // the first 'then' rejects to here
Container.DB.query(query, data) // broken promise: anti-pattern
.then((response) => {
console.log("Resolved", response);
return resolve(response);
}, (error) => {
console.log("REJECTION ERROR", error);//<--the error is consumed here and will
// not go any further on this chain
return reject('An unknown error occurred and has been reported.');
})
.catch((err) => { // this will not be called
console.log("CAUGHT ERROR", err); // because it is the 'onRejected'
// argument of a then
});
})
.catch((err) => { // this will be called and the error will be consumed
console.log("CAUGHT ERROR", err);
return 'An unknown error occurred and has been reported.';
});
;
}
Less worse, you can log and re-throw the error in one catch...
this.createUser = function (data) {
var query = "INSERT into users SET ?";
return new Promise((resolve, reject) => { // this is still redundant
Container.DB.query(query, data) // broken promise: anti-pattern
.then((response) => { // on error, skip this because it has no
console.log("Resolved", response); // 'onRejected' argument
return resolve(response);
})
.catch((err) => { // this will be called and the error
console.log("CAUGHT ERROR", err); // will be consumed
return reject('An unknown error occurred and has been reported.');
});
})
;
}
Better, eliminate the anti-pattern and propagate the message with a throw instead of a reject on the (redundant) promise wrapper...
this.createUser = function (data) {
var query = "INSERT into users SET ?";
return Container.DB.query(query, data)
.then((response) => { // on error, skip this because it has no
console.log("Resolved", response); // 'onRejected' argument
return resolve(response);
})
.catch((err) => { // this will be called and the error
console.log("CAUGHT ERROR", err); // will be consumed so need to re-throw
throw new Error('An unknown error occurred and has been reported.');
});
}
Bearing in mind that a catch is just syntactic sugar for then(undefined, reject) and that, once rejected, a promise is no longer pending, calling it's then method will return undefined 'as soon as possible'. So you can chain another then after the catch if you prefer not to throw...
this.createUser = function (data) {
var query = "INSERT into users SET ?";
return Container.DB.query(query, data)
.then((response) => { // on error, skip this because it has no
console.log("Resolved", response); // 'onRejected' argument
return resolve(response);
})
.catch((err) => { // this will be called and the error
console.log("CAUGHT ERROR", err); // will be consumed. The returned promise
}) // state will be rejected but not pending
// It's then method returns 'undefined' as
// soon as possible
.then(() => 'An unknown error occurred and has been reported.');
}
Taking it one step further, bearing in mind that the value returned by a resolved or rejected promise is the return value of whichever of those is called, you can pass any value you like to the consumer via the return in the catch...
this.createUser = function (data) {
var query = "INSERT into users SET ?";
return Container.DB.query(query, data)
.then((response) => { // on error, skip this because it has no
console.log("Resolved", response); // 'onRejected' argument
return resolve(response);
})
.catch((err) => { // this will be called and the error
console.log("CAUGHT ERROR", err); // will be consumed. The returned promise
// state will be rejected but not pending
// but you can still return any value
return 'An unknown error occurred and has been reported.'
})
}