I have an existing Node Express Application and want to improve the error handling better. My current route endpoint definition is like below,
app.get('/example/getActiveId', async (req, res, next) => {
// Some code to fetch some details from request and do some validations
try {
const result = await api.getActiveId(id);
res.json({ success: true, result }); // I am getting this response in all the time.
} catch (err) {
console.log('getActiveId', err)
console.error(err);
res.json({ success: false });
}
});
Also, I defined error middleware at the last of all the route paths.
// error handler middleware
app.use((error, req, res, next) => {
console.log('in Error middleware')
console.error(error.stack);
res.status(500).send(error.message || 'Something Broke!');
})
My definition of getActiveId is as below.
exports.getActiveId = id => axiosInstance
.get('/example')
.then(({ data }) => data)
.catch(er => er);
The problem in the above getActiveId definition is every time the catch of the getActiveId, the execution falls into the try block of the above endpoint definition. I wanted the execution should go into the catch block endpoint definition function. So that I could call next(err) to call the default express error handling middleware.
So I tried the following mockup code to mimic the same with promise reject.
exports.getActiveId = id => {
const __mockPromise = () => {
return new Promise((resolve, reject) => {
reject('Problem in getActiveId')
})
}
return new Promise((resolve, reject) => {
__mockPromise().then(({ data }) => resolve(data)).catch(er => { console.log('in catch....'); reject(er) })
});
}
I expected the above function will go into the catch block of the end point function definition.
But this time I am getting the following error,
in catch....
(node:32897) UnhandledPromiseRejectionWarning: Problem in getActiveId
(node:32897) 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: 4)
How can I fix this error and bypass the execution to error middleware?
With your current code, api.getActiveId always returns a resolved promise
if the axiosInstance.get succeeds, it resolves to the data
if the axiosInstance.get fails, the .catch(er => er) makes it resolve to the er.
If you want api.getActiveId to return a promise that is rejected with er, omit the .catch(er => er).
For example, if you run Node.js with the following input
const getActiveId = () => Promise.reject("error")
.then(({ data }) => data);
async function test() {
try {
const result = await getActiveId();
console.log(result);
} catch (err) {
console.error(err);
}
}
test();
the console.error statement will be reached and no unhandled promise rejection will be reported.
Related
My unit test code is like this:
// function for my unit test to test exception
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = () => new Promise((resolve, reject) => {
(async () => {
const res = await mockServiceThrow();
if (res === 1) resolve(1)
else reject(0);
})();
});
createContextAndDoc().catch((e) => {
console.log('--------');
console.log(e.message);
console.log('--------');
});
When i run this unit test:
./node_modules/.bin/jest local_modules/__test__/unhandledException.test.js
The complete output is like this:
RUNS local_modules/__test__/unhandledException.test.js
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 "Error: unit test error message".] {
code: 'ERR_UNHANDLED_REJECTION'
}
Don't see why it says unhandled, i do have .catch(). Any suggestions ?
Looks like you're invoking the async function right away, which causes the unit test error, and that is never cought since the promise is never returned - so the .catch is not catching anything. The error is not part of the promise chain.
If you want to invoke the function right away you need to catch the error and reject it so the promise finishes.
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = () => new Promise((resolve, reject) => {
(async () => {
try {
const res = await mockServiceThrow();
if (res === 1) resolve(1)
else reject(0);
} catch (e) {
reject(e)
}
})();
});
createContextAndDoc().catch(e => {
console.log('------------------');
console.log(e.message);
console.log('--------')
})
You could also simplify your code a little:
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = async () => {
const res = await mockServiceThrow();
if(res === 1) {
return Promise.resolve(1)
} else {
return Promise.reject(0);
}
};
createContextAndDoc().catch(e => {
console.log('------------------');
console.log(e.message);
console.log('--------')
})
Edit: Further explanation of promises.
createContextAndDoc is a function that returns a promise. That promise resolves if res === 1 but rejects if res is something else. For that to happen the mockServiceThrow promised must be resolved. Otherwise you won't get any value for res variable.If that happens then your promise function never fullfills (resolves or rejects).
In your case mockServiceThrow fails and throws an error, this error is not part of the promise you created with new Promis. To make sure your promise fullfills (resolves or rejects) you need the callbacks, otherwise the error is not part of the promise.
The simplified code has one async function so that when mockServiceThrow fails its part of the async function that you're trying to catch.
Getting an exception in my Node JS Express application and cannot figure out why. I was getting the same error with my "real" code, so I found this code online and made a test router to see if the error occurred with known good code. Code runs fine in plnkr without the router.post line. Could this be because of the function in the first line? Any help is greatly appreciated.
router.post('/addTableTest', (function (req, res, next) {
let promise1 = new Promise((resolve, reject) => {
let data = false;
if (data) {
resolve('Data');
}
if (!data) {
reject('Not Data');
}
})
promise1.then((message) => {
console.log(message);
}).catch((message) => {
console.log(message);
})
}));
The closure executed by new Promise() is executed synchronously, i.e. immediately after the Promise has been created and before new returns. As your closure has been written to fail immediately and you can't attach a .catch() to it before new returns, you get an unhandled Promise rejection exception.
To make your code work you need to
start a Promise chain by creating a resolved Promise
attach a .then() clause to wrap your synchronous code
replace resolve(X) with return X
replace reject(X) with throw new Error(X)
Now you can safely attach the other Promise clauses, because the code in the just created Promise chain won't be executed until the closure that has created it leaves.
router.post('/addTableTest', (req, res, next) => {
let promise1 = Promise.resolve()
.then(() => {
let data = false; // i.e. the promise will reject
if (data) {
return 'Data';
} else {
throw new Error('Not Data');
}
});
promise1
.then(message => {
console.log(message);
})
.catch(error => {
console.log(error.message);
});
});
I am unable to figure out how to break a nested promise chain into a main promise chain. Here is my code :
//Main Promise chain
let promiseMain = Promise.resolve(1)
.then(result => functionA())
.then(result => nestedChain()).catch((error) => {
console.log(error);
})
.then(result => functionC())
//chain error handler
function chainError(err) {
return Promise.reject(err)
};
function nestedChain()
{
stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError)
.catch((error) =>
{
console.log(error);
return undefined;
});
}
function stepOne()
{
return chainError("error attempt : 1.00");
}
Once I go into the nestedChain where I throw an error in stepOne() I am able to break this nestedChain. Awesome!
The problem: It also breaks the main promise chain. So.. when it goes into nestedChain() and the error is thrown from stepOne(), functionC from the main promise chain will never be executed because the rejected promise from the nestedChain also break this chain.
You will have to attach with promises chain, do something like this
function nestedChain()
{
stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError)
.catch ((error) => {
console.log(error);
return undefined;
})
}
Promise are implemented to wait for something which take more time. You just need to implement promise correctly.
For example if we have 3 function returning promise and one nested function also returning promise, this is how it's implemented:
functionA()
.then( result => {
return functionB();
})
.then( result => {
return nestedFunct();
})
.then( result => {
return functionC();
})
.then( result => {
console.log(result);
})
.catch( error => {
console.log(`Error in one of the above function`);
});
All the function are of format similar too
function functionA() {
return new Promise( (resilve, reject) => {
resolve('something');
// or
reject('Error');
});
}
Nested function can be like this
function nestedFunc() {
return functionD() // Return promise
.then( result => {
return functionE(); // return promise
})
.then( result => {
return functionF(); // return promise or value
});
}
Main promise chain is not affected by what individual function do as long as they keep returning promise. All individual function can have a local promise chain. Even if an error occurred in nested chain will be catched by catch() in main promise chain.
If i understood you correct, you need to catch error after nested chain promise
Promise.resolve().then(()=> {
console.log('A');
}).then(() => {
return Promise.resolve().then(() => {
console.log('step 1');
}).then(() => {
console.log('step 2');
throw new Error('Step 2 error');
});
}).catch((err) => {
console.log(err);
}).then(() => {
console.log('C');
});
//foo.js
const api = require('someApi');
exports.get = obj => {
if (!obj.id) {
return Promise.reject('no id');
}
api.get(obj.id)...
}
//foo.spec.js
let getStub;
beforeEach(() => {
getStub = sinon.stub(api, 'get');
});
it('should fail to retrieve location on insufficient data', () => {
let obj = {};
foo.get(obj)
.catch(err => {
getStub.should.have.not.been.called();
expect(err).to.not.equal('undefined');
})
});
When i execute the test I receive this error:
(node:73773) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): TypeError: Cannot read property 'have' of undefined
There is no other stack trace to the error. As far as i can see I have handling the promise rejection by catching the error in the catch block.
Is this a good way to test and how should I fix this ?
This is usually because the "it" block finished before the catch block catches the exception. You can add a 'done' call to prevent this:
it('should fail to retrieve location on insufficient data', (done) =>
{
let obj = {};
foo.get(obj)
.catch(err => {
getStub.should.have.not.been.called();
expect(err).to.not.equal('undefined');
done();
})
}
When a user logs in with incorrect email and password, the success block of my client-side promise still executes, even though the server returned a 400.
I'm using Redux with React so I'm calling an action creator which calls an HTTP request using axios. I need help understanding why I'm not handling errors correctly, because the rest of my app's auth functions like signup, logout, etc all behave the same way even though I'm returning 400 statuses from the server.
Here is where I call login from my component, the success block always executes:
handleFormSubmit({
email, password
}) {
this.props.loginUser({
email, password
}).then(() => {
toastr.success("You are logged in!");
}).catch(() => {
toastr.warning("Could not log in");
})
}
Here is the action creator "loginUser", the success block for this function does NOT run when I return a 400 from the server:
export function loginUser({ email, password }) {
return function(dispatch) {
return axios.post(`/users/login`, {
email, password
})
.then(response => {
dispatch({
type: AUTH_USER
});
localStorage.setItem('token', response.headers['x-auth']);
browserHistory.push('/feature');
})
.catch(() => {
dispatch(authError('Incorrect email or password'));
});
}
}
Here is the route '/users/login' Please note that the 400 status does in fact return:
app.post('/users/login', (req, res) => {
var body = _.pick(req.body, ['email', 'password']);
User.findByCredentials(body.email, body.password).then(user => {
return user.generateAuthToken().then(token => {
res.header('x-auth', token).send(user);
});
}).catch(e => {
res.status(400).send();
});
});
You issue is that you're misunderstanding what a catch clause is in promises.
The way you can think if it is just a then with a rejection handler:
.then(null, function(err) {
// handle the error
})
Meaning it only handles the last unhandled error from the promise chain, and you can continue chaining after it no matter what happened.
Example:
new Promise((resolve, reject) => {
setTimeout(() => reject(Error('After 1 sec')), 1000)
})
.catch((err) => {
console.log(`catch: ${err}`);
return 5;
})
.then((five) => {
// this chains because the error was handled before in the chain
console.log(`catch.then: ${five}`); // 5
})
.catch(() => {
console.log('No error happened between this error handler and previous so this is not logged');
});
To make an error propagate from the current catch to the next error handler, you can return a rejected Promise (or re-throw the error) to make the chain skip all the success handlers until the next fail (or catch) handler.
new Promise((resolve, reject) => {
setTimeout(() => reject(Error('After 1 sec')), 1000)
})
.catch((err) => {
// return a reject promise to propagate to the next error handler
return Promise.reject(err);
// can also `throw err;`
})
.then((nothing) => {
// this doesn't happen now
console.log(nothing);
})
.catch(console.error); // this logs the error
Side note: When you don't provide a rejection handler in a Promise chain (the second parameter in .then), the default rejection handler essentially behaves like:
function defaultRejectionHandler(err) {
throw err;
}
Which means it re-throws whatever error was passed into it so the error can propagate to the next error handler that you do specify.
The problem is in your catch handler of loginUser function.
If you want to catch the error farther down the promise chain, you'll need to throw the error in the catch block.
.catch(() => {
dispatch(authError('Incorrect email or password'));
throw Error('Incorrect email or password');
});