I am working in a new codebase that has a pattern where some reusable functions return a promise from a chain, but they don't handle errors.
Here is an example:
function createScheduleForGroup(group) {
if (notInProduction()) {
group.gschedScheduleId = '0';
return Bluebird.resolve(group);
}
// note: schedule is a global within the file
return schedule.createSchedule(group.name).then(function (schedule) {
group.gschedScheduleId = schedule.id;
return group.save();
});
}
In the example above, there isn't a .catch or a reject function passed to the .then.
The function is eventually used in an express route where the error is handled:
router.post('/schedule', function(req, res, next) {
scheduleLogical
.createScheduleGroup(req[config.entity])
.then(function(group) {
res.status(201).json(group);
})
.catch(next);
// if creteScheduleGroup throws an error it is handled here
Is it a common pattern to not define error handlers for a promise returned from a function, and anticipate whoever uses the function to attach the appropriate error handlers?
To make this clearer for my own understanding, I made a simulation of this specific function and all the functions used within the promise chain. Here it is:
function getScheduleMock() {
// this promise promisifys an older api that uses callbacks
return new Promise((resolve, reject) => {
// note this is simulating an api call:
const response = Math.round(Math.random());
// 0 === err, 1 === success
if(response === 0) return reject(response);
resolve(response);
})
// error hanlding :)
.catch(err => {
console.log(err);
return Promise.reject(err);
// there is an error handling function that logs the errors. If the error doesn't match expected errors, it rejects the error again as I have here.
})
.then(responseData => {
return Promise.resolve(responseData);
})
}
function createScheduleForGroupMock() {
return getScheduleMock().then(responseData => Promise.resolve('everything worked :)'));
// Note: group.save() from the original returns a promise
// just like the actual example, the promise is returned
// but there isn't any form of error handling except within the getScheduleMock function
}
createScheduleForGroupMock(); // when the error is rejected in the .catch() in getScheduleMock, the error is unhandled.
/* ***************** */
/* the createScheduleForGroup method is used within an express route which has error handling, here is an example of the code: */
// router.post('/schedule', function(req, res, next) {
// scheduleLogical
// .createScheduleGroup(req[config.entity])
// .then(function(group) {
// res.status(201).json(group);
// })
// .catch(next);
// if creteScheduleGroup throws an error it is handled here
I'm fairly new to error handling promises and from what I've been reading and practicing, I generally felt you should always include an error handler. The codebase I'm working in has a lot of methods that use the pattern of createScheduleForGroup where there isn't an error handler defined in the function, but instead, it is handled and attached after the function is used.
Some of the functions used within createScheduleForGroup handle their own errors, and I'm confused and curious about the balance on when to handle errors in a function that returns a promise and when to attach them when you use the function.
Is it a common pattern to not define error handlers for a promise returned from a function, and expect whoever uses the function to attach the appropriate error handlers?
Yes, totally. It's not just "a common pattern", it's the absolute standard pattern.
Just like you don't put a try/catch statement in every synchronous function, you don't put a .catch callback on every promise that you return. In fact, it's considered an antipattern to catch errors that you cannot handle.
You can have an error handler in each function.
function aPromise() {
return new Promise(function(resolver, reject){
//Handle any error here and attach different information via your own errror class
})
}
async function parentProcess() {
try{
await aPromise()
}
catch(e) {
//Handle and attach more info here
}
}
function grandParentProcess() {
try{
parentProcess();
}
catch(e){
//Handle the error
}
}
You don't essentially have to handle error in the parent function if the grand parent function handles it to avoid the UnhandledPromiseRejection error.
Related
I am new to typescript / javascript, so I don't know much about promises. Here is my use-case: I am creating three different promises inside my cloud-function and then returning it with Promise.all([promise1, promise2, promise3]). Each of these promises are created inside a function with "return Promise...".
My question is, when I add ".catch" inside these functions, will Promise.all still work?. Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
Code
export async function myCloudFirestoreFunction() {
try {
const myFirstPromise = createFirstPromise()
const mySecondPromise = createSecondPromise()
const thirdPromise = createThirdPromise()
return Promise.all([
myFirstPromise,
mySecondPromise,
myThirdPromise
]);
} catch (err) {
functions.logger.err(`Something bad happened, See: ${(err as Error).message}`
}
}
// Difference with and without `.catch`?
function createFirstPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createSecondPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
// Difference with and without `.catch`?
function createThirdPromise() {
return someServiceThatCreatesPromise().catch((err) => { // LOGGING });
}
Adding .catch inside createNPromise won't affect anything assuming all your Promises resolve and do not reject.
However, if one of the Promises rejects and you catch it within the .catch method, then it won't work as you're expecting unless you re-throw that error and catch it again inside the try/catch in your myCloudFirestoreFunction function.
async function myCloudFirestoreFunction() {
try {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]);
} catch (error) {
console.log({ error });
}
}
function createFirstPromise() {
return Promise.reject("Oof").catch((e) => {
// do work, e.g. log, then
// pass the error forward so that it can be caught
// inside the caller
throw e;
});
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
Alternatively, you just catch errors inside the caller (myCloudFirestoreFunction) instead of catching them separately.
async function myCloudFirestoreFunction() {
const result = await Promise.all([
createFirstPromise(),
createSecondPromise()
]).catch((err) => console.log({ err }));
}
function createFirstPromise() {
return Promise.reject("Oof");
}
function createSecondPromise() {
return Promise.resolve("value");
}
myCloudFirestoreFunction();
when I add ".catch" inside these functions, will Promise.all still work?
Calling catch() on a promise does not in any way change the way the original promise works. It is just attaching a callback that gets invoked when the first promise becomes rejected, and also returning another promise that resolves after the original promise is fulfilled or rejected.
Does it make any difference returning someServiceThatCreatesPromise() with and without .catch()?
It would not make any difference to the code that depends on the returned promise. Both the original promise and the one returned by catch() will tell downstream code when the original work is done by becoming fulfilled.
I suggest reading comprehensive documentation on promises, for example:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
There is a diagram in there that you can follow to understand what happens at each turn. Also in that document, you will read:
Processing continues to the next link of the chain even when a .then() lacks a callback function that returns a Promise object. Therefore, a chain can safely omit every rejection callback function until the final .catch().
Background:
I have a gateway that returns the status code 200 via a post request if it is completely booted. If not it returns 500 while booting.
My idea:
I have a NodeJS application which should wait until the gateway returns 200. So I created a while loop which checks the state of the gateway.
My problem:
Unfortunately nothing works, the state is always true. Non of the log statements in the request will be display.
Do you have tips for me how I can fix this?
while (isGatewayUnavailable()) {
log.info('waiting for gateway ...');
sleep(60)
}
function isGatwayUnavailable() {
const url = '...'
let state = true
request.post(url, (err, res, body) => {
log.debug(0)
if (err) {
log.debug("Gateway offline");
log.debug("err: " + err);
}
else if (res.statusCode === 200) {
log.debug("Gateway online");
state = false;
cb(true);
}
else {
log.debug("Status Code: " + res.statusCode);
}
});
log.debug('return state: ' + state);
return state;
}
There is no "waiting" in JS. There's only "running code" and "running code in response to signals" (events, callbacks, promises). In this case, you want to do something based on a process that you do not control the timing of, so you can't use a synchronous function: by the time the function reaches its return keyword, you don't have any information to return yet..
So, instead of making your function return a value and having the caller wait for that value, make your code "do things once the information is in". That is, make your function either generate an event that you have a handler registered for, or pass a callback as argument so that your function can run that callback once it has the information necessary, or have it return a promise whose resolve (or reject) gets called once you have the information necessary.
1. Event-based:
const pubsub = ...;
function checkGatwayAvailability() {
request.post(url, (err, res, body) => {
pubsub.signal("gateway:availability", { available: ..., error: ... });
});
}
with caller code:
const pubsub = ...;
pubsub.register("gateway:availability", data => {...});
...
checkGatewayAvailability();
In this, the code that calls this and the code that handles the result are 100% detached from each other. Also note that pubsub isn't a real thing. Depending on your framework and APIs, there will be different ways to achieve event generation/handling, or you might even need to write your own (which really means "hit up npm and find one that is well documented and used by many folks, then use that").
2. Using a callback:
function checkGatwayAvailability(reportResult) {
request.post(url, (err, res, body) => {
reportResult({ available: ..., error: ... });
});
}
with caller code:
checkGatwayAvailability( result => {
...
});
In this approach, the calling and handling code are coupled in the sense that your call points to the handler, even if your handler is declared somewhere completely different, like:
checkGatwayAvailability(NetworkMonitor.handleGatewayResponse);
3. Using a promise:
function checkGatwayAvailability(reportResult) {
return new Promise((resolve, reject) => {
request.post(url, (err, res, body) => {
if (err) reject(err);
resolve(...);
});
});
}
with caller code:
checkGatwayAvailability().then(result => {...}).catch(err => {...});
Similar to a callback, the calling and handling code are coupled, but with promises you don't "guess" at whether the resultant information is good or bad, you literally have separate code paths for the "good" cases (handled by then), and the "bad" cases (handled by catch).
3b. Using a promise through async/await syntax:
In this case, request.post does not return a Promise, your function would still need to bake its own promise, so using an async declaration doesn't make a lot of sense. We can still use the await keyword in the calling code, though:
try {
const result = await checkGatwayAvailability();
} catch (e) {
...
}
but only if that caller code itself runs inside an async context.
This question already has an answer here:
Using Q.promises: how to catch an async throw?
(1 answer)
Closed 6 years ago.
I know that stackoverflow is full of similar question and I've read a lot of them.
From what I got a throw inside a promise should reject it, as I can read in the documentation:
If the executor throws an exception, its value will be passed to the reject resolving function.
But even after read a lot of post about promises and throw I still don't understand the snippet of code I'm pasting and why it happens.
function foo(a, b, cb) {
setTimeout(() => {
cb('Inner error *!?"$%&##"');
}, 0);
}
const getThePromise = () => {
return new Promise((resolve, reject) => {
const cb = (err) => {
/* >>> ************ */
throw err; // catch not called
// reject(err); // catch called
/* ************ <<< */
}
foo('foo', 'dudee', cb);
});
}
getThePromise()
.catch((err) => {
console.log('CATCH:', err);
})
.then((res) => {
console.log('then...');
})
I don't understand why if I use the throw the .catch of the promise is not called but if I use the reject it is called.
Just for sake of clarification I'm using Node.js v6.2.2 in a Mac OS/X 10.11 but I don't think it could be also a browser issue.
You are throwing your error inside an asynchronous setTimeout call, which will lead to an uncaught error. The asynchronous code will not execute in the same context as the try-catch block. This has nothing to do with the promise API. This is just part of the behavior of asynchronous code execution in JavaScript.
Take a look at the following example.
const asyncOperation = err => {
try {
setTimeout(function() {
throw err; // will be dropped onto the event queue
// until the call stack is empty
// even if this takes longer than
// a second.
}, 1000);
} catch (e) {
console.log(e) // will not be called
}
}
asyncOperation('Inner error *!?"$%&##"')
And now the same example with the try-catch block inside the setTimeout call and the error being thrown inside the try block.
const asyncOperation = err => {
setTimeout(function() {
try {
throw err // here the error will be throw inside
} catch (e) { // the try block and has the same execution
console.log(e) // context.
}
}, 1000);
}
asyncOperation('Inner error *!?"$%&##"')
You can find more information regarding the Promise.catch right here.
Promise.prototype.catch()
The catch() method returns a Promise and deals with rejected cases only.
There is actually an example with the same situation you are describing in your example. Check out
Gotchas when throwing errors
// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw 'Uncaught Exception!';
}, 1000);
});
p2.catch(function(e) {
console.log(e); // This is never called
});
The following code:
function handleError(res, statusCode) {
statusCode = statusCode || 500;
return function(err) {
res.status(statusCode).send(err);
};
}
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
}
};
}
// Creates a new Store in the DB
export function create(req, res) {
// create user
let user = req.body.user;
let store = req.body.store;
auth.hash(user.password)
.then(hash => {
user.password = hash;
// Create user, then create store, attach store object id to user, and attach user object id to store
User.create(user)
.then(userRes => {
store.owner = userRes._id;
store.memebers = [];
store.memebers.push(store.owner);
Store.create(store)
.then(storeRes => {
return respondWithResult(res, 201);
})
.catch(err => handleError(err));
})
.catch(err => handleError(err));
})
.catch(err => handleError(err));
}
prints the error mentioned in the title, "(node:5540) Warning: a promise was created in a handler but was not returned from it". I have tried changing and tweaking the code but the error still persists.
This warning is because your code is creating promises inside of .then() handlers, but not returning them from those handlers.
Change:
User.create(user)
to:
return User.create(user)
And, change:
Store.create(store)
to
return Store.create(store)
When you don't return these promises that are created inside .then() handlers, they become separate, independent promise chains and are not linked to the previous promise chain. This is usually a programming mistake which is why Bluebird makes it a warning.
When you return them, then they add to the promise chain and thus the parent promise waits for their completion before continuing on with the chain.
I'd also suggest you probably want to change:
auth.hash(user.password)
to:
return auth.hash(user.password)
So that the caller of create() can tell when everything is done.
And, you only need one .catch() handler at the highest level. Rejected promises propagate up to the top level for you automatically (one of the things that makes error handling when using promises easier).
This is a warning message when you don't return to a request. Of course this seems just another warning but when you work on a big application this becomes a very big headache because this will lead to a memory leak and wont release the memory until you restart your app or might crush your server.
you also need to return for else statments:
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
}else{
//you should write an else statement also
//maybe something like this
res.status(statusCode).send(err);
}
};
}
Return to your request in every case.
Does anybody have any thoughts about ES6 promises, I'm using them in my Node app and I love them, on the most part. But Ive found that if I get some sort of error in a resolve callback, it won't throw an error or execute the reject callback, and its leaving me with my server hanging infinitely.
For now I've resorted to doing this, and manually rejecting the promise with the caught error, but I'm not sure if this a great way to handle, and/or if I should be using promises at all.
this.dataStore.set(newID, value).then( (foo) => {
try{
this.var = foo;
res({val: foo});
}catch(e){
rej(e);
}
}, (e) => {
rej(e);
});
I think the confusion is arising from the fact that, based on your use of res, and rej here, you are likely calling this from within a promise constructor, along the lines of
function setStore(newID, value) {
return new Promise(function(res, rej) {
this.dataStore.set(newID, value).then( (foo) => {
try{
this.var = foo;
res({val: foo});
}catch(e){
rej(e);
}
}, (e) => {
rej(e);
});
});
}
By the way, the (e) => { rej(e); } part at the end could be rewritten as e => rej(e), which in turn could be rewritten as rej.
But anyway, you don't need any of that surrounding machinery to create and return your own promise, because this.dataStore.set and/or the ensuing call to then already creates a promise, which you can return as is. Instead of creating your own new promise, and then resolving your new promise with the little hash based on the result passed to then, just return the hash--that will become the value of the resulting promise. Instead of rejecting your new promise when the call to dataStore.set fails, just let the failed promise be itself.
So you shouldn't need to do anything more complicated than
function setStore(newID, value) {
return this.dataStore.set(newID, value).then(foo => {
this.var = foo;
return {val: foo};
});
}
An error occurring in the this.var = foo; return {val: foo}; part (but how could it?) will automatically throw the promise into failure state. A failure resulting from this.dataStore.set will yield a failed promise as is, and there is no need to catch it and rethrow it--the failure will progress merrily down the chain.
Use this as:
setStore('abc', 123)
.then(hash => console.log(hash.val))
.catch(e => console.log("The sky is falling", e));
As a matter of clarification, in the following:
promise.then(success, failure)
an error arising in the success callback is not handled in the failure callback. A failure in the success callback would be handled in successive stages of the chain. You could handle a failure in success (or promise itself) with
promise.then(success).catch(failure)