The motivation for this is to be able to catch all possible errors with the ending .catch, even ones that happen in initial synchronous code.
I want to start my promise chain like so:
const bbPromise = require('bluebird');
bbPromise.do(() => {
someTask(); // Could throw
return someVar.doSomeOtherTaskAsync();
})
.then((result) => {
// Do something with result
})
.catch((err) => {
console.log('err: ', err);
});
Is there a function that works like bbPromise.do function? bbPromise.resolve just gives me the whole lambda function that was passed in, unexecuted.
I know I could do something like this:
bbPromise.bind({}).then(() => {
someTask(); // Could throw
return someVar.doSomeOtherTaskAsync();
})
...
or even:
new bbPromise((resolve, reject) => {
someTask(); // Could throw
return someVar.doSomeOtherTaskAsync().then(resolve).catch(reject);
})
...
But these are a little indirect. Is there a good way to start a promise chain by just executing a function that could return a promise, and that has errors caught in the ending .catch?
It sounds like you're looking for Promise.try. Promise.resolve is appropriate when you have a value already and want to build a promise chain from it, but if you want to run some code that may throw, use Promise.try instead.
try takes and executes a function, dealing with any synchronous exception in the same manner as then would. You would end up with something like:
bbPromise.try(someTask).then(() => {
return someVar.doSomeOtherTaskAsync();
}).then((result) => {
// Do something with result
}).catch((err) => {
console.log('err: ', err);
});
Related
This is a design question that came up to me while unit testing.
Let's dive into the example:
Imagine this:
async function foo() {
try {
return apiCall()
}
catch (e) {
throw new CustomError(e);
}
}
async function bar() {
return foo()
}
async function main() {
try {
await bar()
}catch(e) {
console.error(e)
}
}
main()
What do we see here? That the only function that hasn't got a try-catch block is bar.
But if foo fails, it should get catched by the main catch.
While unittesting this like
describe('testing bar', () => {
it('foo should throw', () => {
foo.mockImplementantion(() => { throw new CustomError('error')});
bar()
.then((result) => console.log(result))
.catch((err) => { exepect(err).toBeInstanceOf(CustomError)}) // this is what we are testing
})
})
The output we see is that an Unhandled promise rejection is logged in the console.
So, my question is... even if I know that the main() will catch the error, should I use try-catch block inside all async functions?
try..catch may be necessary if a function is able to recover from an error, do a side effect like logging, or re-throw a more meaningful error.
If CustomError is more preferable than an error that apiCall can throw then try..catch necessary, otherwise it doesn't. Also the problem with foo is that it handles only synchronous errors. In order to handle rejected promises, it should be return await apiCall(), this is a known pitfall of async.
Uncaught rejections are unwanted, they currently result in UnhandledPromiseRejectionWarning and are expected to crash Node in future versions. It's preferable to handle an error in a meaningful way at top level, so main needs to catch the error. This can be delegated to process uncaughtRejection event handler but it may be beneficial for it to stay extra level of error handling that should be never reached.
The output we see is that an Unhandled promise rejection is logged in the console.
This shouldn't happen. A rejection needs to be handled by the test. One possible point of failure is explained above, foo can return original error from apiCall instead of CustomError in case it wasn't correctly mocked, this will fail the expectation and result in unhandled rejection in catch(). Another point of failure is that the test has unchained promise because it wasn't returned, the test always passes.
Asynchronous test that uses promises should always return a promise. This can be improved by using async..await. foo is async, it's expected to always return a promise:
it('foo should throw', async () => {
foo.mockImplementantion(() => { return Promise.reject(new CustomError('error')) });
await expect(bar()).rejects.toThrow(CustomError);
})
Now even if foo mock fails (foo mock won't affect bar if they are defined in the same module as shown) and bar rejects with something that is not CustomError, this will be asserted.
No. You don't need to use try/catch in every async/await. You only need to do it at the top level. In this case your main function which you are already doing.
Weather you should is a matter of opinion. The go language designers feel strongly enough about this that is has become the standard in go to always handle errors at each function call. But this is not the norm in javascript or most other languages.
Unhandled promise rejection
Your unhandled promise rejection is thrown by your it() function because you are not telling it to wait for the promise to complete.
I assume you are using something like mocha for the unit test (other frameworks may work differently). In mocha there are two ways to handle asynchronous tests:
Call the done callback - the it() function will always be called with a done callback. It is up to you weather you want to use it or like in your posted code to not use it:
describe('testing bar', () => {
it('foo should throw', (done) => {
foo.mockImplementantion(() => { throw new CustomError('error')});
bar()
.then((result) => {
console.log(result);
done(); // ------------- THIS IS YOUR ACTUAL BUG
})
.catch((err) => {
exepect(err).toBeInstanceOf(CustomError);
done(); // ------------- THIS IS YOUR ACTUAL BUG
})
})
})
Return a Promise. If you return a promise to the it() function mocha will be aware that your code is asynchronous and wait for completion:
describe('testing bar', () => {
it('foo should throw', (done) => {
foo.mockImplementantion(() => { throw new CustomError('error')});
return bar() // <----------- THIS WOULD ALSO FIX IT
.then((result) => {
console.log(result);
})
.catch((err) => {
exepect(err).toBeInstanceOf(CustomError);
})
})
})
In short, there is nothing wrong with your code. But you have a bug in your unit test.
As #Bergi told me I will post some solutions right here
I wrap the function in a try catch block
1.
async function bar() {
try{
return foo()
} catch (e) {
throw e
}
}
Rewrite the test
describe('testing bar', () => {
it('foo should throw', (done) => {
foo.mockImplementantion(() => { throw new CustomError('error')});
bar()
.then((result) => { throw result }) // this is because we are expecting an error, so if the promise resolves it's actually a bad sign.
.catch((err) => {
exepect(err).toBeInstanceOf(CustomError)}) // this is what we are testing
done();
})
})
Use return in the test case
describe('testing bar', () => {
it('foo should throw', () => {
foo.mockImplementantion(() => { throw new CustomError('error')});
return bar()
.then((result) => { throw result })
.catch((err) => { exepect(err).toBeInstanceOf(CustomError)}) // this is what we are testing
})
})
I have the following code that uses callbacks inside promises:
const clue = 'someValue';
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(null, array); // Callback with 'Success'
}
else{
callback(`The array does not includes: ${clue}`); // Callback with Error
}
})
.catch((err) => {
// handle error
callback(`Some error inside the promise chain: ${err}`) // Callback with Error
})
}
and call it like this:
myFunction (someParam, (error, response) => {
if(error) {
console.log(error);
}
else {
// do something with the 'response'
}
})
Reading some documentation, I found that there is some improved way to do this:
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(array);
}
else{
callback(`The array does not includes: ${clue}`);
}
}, (e) => {
callback(`Some error happened inside the promise chain: ${e}`);
})
.catch((err) => {
// handle error
callback(`Some error happened with callbacks: ${err}`)
})
}
My question:
In the sense of performance or best practices, it's okay to call the 'callback' function inside the promise as the two ways show or I'm doing something wrong, I mean some promise anti-pattern way ?
This seems really backwards and takes away from the benefits of promises managing errors and passing them down the chain
Return the asynchronous promise from the function and don't interrupt it with callbacks. Then add a catch at the end of the chain
const myFunction = (someParam) => {
// return the promise
return someAsyncOperation(someParam) // this function returns an array
.then((array) => {
return array.includes(clue) ? array : [];
});
}
myFunction(someParam).then(res=>{
if(res.length){
// do something with array
}else{
// no results
}
}).catch(err=>console.log('Something went wrong in chain above this'))
Do not use callbacks from inside of promises, that is an anti-pattern. Once you already have promises, just use them. Don't "unpromisify" to turn them into callbacks - that's moving backwards in code structure. Instead, just return the promise and you can then use .then() handlers to set what you want the resolved value to be or throw an error to set what you want the rejected reason to be:
const clue = 'someValue';
const myFunction = (someParam) => {
return someAsyncOperation(someParam).then(array => {
if (!array.includes(clue)){
// reject promise
throw new Error(`The array does not include: ${clue}`);
}
return array;
});
}
Then, the caller would just do this:
myFunction(someData).then(array => {
// success
console.log(array);
}).catch(err => {
// handle error here which could be either your custom error
// or an error from someAsyncOperation()
console.log(err);
});
This gives you the advantage that the caller can use all the power of promises to synchronize this async operation with any others, to easily propagate errors all to one error handler, to use await with it, etc...
So I have an Express app that uses middleware to parse JSON POST requests and then populate a req.body object. Then I have a promise chain that validates the data against a schema using Joi, and then stores it in a database.
What I would like to do is check if an error was thrown after one of these processes, handle it appropriately by sending a status code, then COMPLETELY ABORT the promise chain. I feel like there should be some EXTREMELY CLEAN AND SIMPLE way to do this, (perhaps some sort of break statement?) but I can't find it anywhere. Here is my code. I left comments showing where I hope to abort the promise chain.
const joi = require("joi");
const createUserSchema = joi.object().keys({
username: joi.string().alphanum().min(4).max(30).required(),
password: joi.string().alphanum().min(2).max(30).required(),
});
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
//CLEANLY ABORT the promise chain here
})
.then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
})
.catch(error => {
res.sendStatus(500);
//CLEANLY ABORT the promise chain here
})
//Only now, if both promises are resolved do I send status 200
.then(() => {
res.sendStatus(200);
}
)
});
You can't abort a promise chain in the middle. It's going to either call a .then() or a .catch() later in the chain (assuming there are both and assuming your promises resolve or reject).
Usually, the way you handle this is you put one .catch() at the end of the chain and it examines the type of error and takes appropriate action. You don't handle the error earlier in the chain. You let the last .catch() handle things.
Here's what I would suggest:
// helper function
function err(status, msg) {
let obj = new Error(msg);
obj.status = status;
return obj;
}
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body).catch(validationError => {
throw err("validateError", 400)
}).then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(err => {
throw err("createUserError", 500);
});
}).then(() => {
// success
res.sendStatus(200);
}).catch(error => {
console.log(error);
if (error && error.status) {
res.sendStatus(error.status);
} else {
// no specific error status specified
res.sendStatus(500);
}
});
});
This has several advantages:
Any error propagates to the last .catch() at the end of the chain where it is logged and an appropriate status is sent in just one place in the code.
Success is handled in just one place where that status is sent.
This is infinitely extensible to more links in the chain. If you have more operations that can have errors, they can "abort" the rest of the chain (except the last .catch() by just rejecting with an appropriate error object).
This is somewhat analogous to the design practice of not having lots of return value statements all over your function, but rather accumulating the result and then returning it at the end which some people consider a good practice for a complicated function.
When debugging you can set breakpoints in one .then() and one .catch() to see the final resolution of the promise chain since the whole chain goes through either the last .then() or the last .catch().
.catch returns a resolved Promise by default. You want a rejected Promsise. So, you should return a rejected promise from inside the .catch, so that future .thens won't execute:
.catch(validationError => {
res.sendStatus(400);
return Promise.reject();
})
But note that this will result in a console warning:
Uncaught (in promise) ...
So it would be nice to add another .catch to the end, to suppress the error (as well as catch any other errors that come along):
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return Promise.reject();
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => void 0);
If you want to avoid all future .thens and future .catches, I suppose you could return a Promise that never resolves, though that doesn't really sound like a sign of a well-designed codebase:
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return new Promise(() => void 0);
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => {
console.log('final catch');
});
A cleaner solution for what you are trying to accomplish might be to use express-validation, which is a simple wrapper around joi that provides you with express middleware for validation of the body, params, query, headers and cookies of an express request based on your Joi schema.
That way, you could simply handle any Joi validation errors thrown by the middleware within your "generic" express error handler, with something like:
const ev = require('express-validation');
app.use(function (err, req, res, next) {
// specific for validation errors
if (err instanceof ev.ValidationError)
return res.status(err.status).json(err);
...
...
...
}
If you don't want to use the express-validation package, you could write your own simple middleware that does more or less the same thing, as described here (see example here).
One strategy is to separate your error handling in subpromises which have their individual error handling. If you throw an error from them, you'll bypass the main promise chain.
Something like:
return Promise.resolve().then(() => {
return createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
throw 'abort';
});
}).then(validatedUser => {
// if an error was thrown before, this code won't be executed
// accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(error => {
// if an error was previously thrown from `createUserSchema.validate`
// this code won't execute
res.sendStatus(500);
throw 'abort';
});
}).then(() => {
// can put in even more code here
}).then(() => {
// it was not aborted
res.sendStatus(200);
}).catch(() => {
// it was aborted
});
You can skip the Promise.resolve().then() wrapping, but it's included for illustrative purposes of the general pattern of subdividing each task and its error handling.
Let's say I have a file that is importing a function from the api and doing something with the result.
import api from './api'
const getData = () => {
api.get(url)
.then(res => console.log(res))
.catch(err => console.log(err))
}
I've seen two different styles for returning the response so the error bubbles up.
version 1:
get: (url) => {
return new Promise((resolve, reject) => axios.get(url)
.then(res => resolve(res))
.catch(err => reject(err)) )
}
version 2:
get: (url) => {
return axios.get(url)
.then(res => {
if (res.success == 200) {
return Promise.resolve(res)
}
return Promise.reject(res)
})
}
What are the differences between the two approaches? Is there a preferred/better method for error handling?
In general, two handy rules:
If your starting point is a promise (the return value of axios.get), then using new Promise is an anti-pattern. then and catch already create new promises.
Propagate errors, don't convert them to resolutions (until, of course, the point at which your handling those errors, usually near the beginning of the call chain).
Based on that, of those two options, you'd use Version 2, not Version 1.
Version 1 is a bit nonsensical: It eats the resolution value and converts errors to resolutions; it will always resolve to undefined. Remember that the result of the chain is the result of the last thing returned (or thrown) in a then or catch handler. Those two handlers don't return or throw anything, so the chain resolves (rather than rejecting) with undefined.
Version 2 does something: It modifies the promise result based on res.success. But it doesn't need Promise.resolve, it should just be:
get: (url) => axios.get(url).then(res => {
if (res.success == 200) {
return res;
}
return Promise.reject(res); // Or see note below
})
And there's a camp — which I agree with — which says you should always throw an Error object rather than returning Promise.reject, for stack trace info. So:
get: (url) => axios.get(url).then(res => {
if (res.success == 200) {
return res;
}
throw new Error(res/*...or something else useful here...*/);
})
There is no real difference with the two versions, at the first one, you are creating a redundant Promise.
axios.get, is a function that returns a Promise there is no need to wrap it with another one.
You can warp with additional Promise if you wanna change the promise logic, meaning that if you wanna resolve no matter of what happened in axios promise.
Something like that:
get: (url) => {
return new Promise((resolve, reject) => axios.get(url)
.then(res => resolve(res))
.catch(err => resolve(err)) )
}
but, usually it is not the practice.
I have nested promise calls structure as follow:
validateUser()
.then(() => {
getUserInformation()
.then((userInformation) => {
Promise.all([
getUserDebitAccounts(userInformation),
getUserCreditAccounts(userInformation)
])
.then(([drAcc, crAcc]) => {
//do something
})
})
})
.catch(error => {
callback(error);
});
First of all is there a way to simplify these nested calls? As you can see they are structure as per dependency. So this is a most logical way I came up.
Second can I use catch at end to catch all the rejects on all above call. Do I have to add separate catch for each calls?
Third I want to write a mocha test what level of Promise mocking I have to do for these methods some outline will be helpful.
Any suggestion?
Yes, you can and should flatten your chain. To do that you'll need to return the inner promises from the then callbacks, which you will need anyway to make the single catch in the end work.
return validateUser()
.then(getUserInformation)
.then(userInformation =>
Promise.all([
getUserDebitAccounts(userInformation),
getUserCreditAccounts(userInformation)
])
).then(([drAcc, crAcc]) => {
// do something
return …;
})
.catch(error => {
console.error(error);
});