Handling Errors in A Chain of Promises - javascript

I am using Q.js as a promises library. Previously, theres code like:
function X() {
return Q.Promise(function(resolve, reject) {
Users.find()
.then(function(user) {
Q.all([
getUserProfileInfo(user),
getUserSomethingElse(user)
])
.spread(function(profile, something) {
// do stuff
resolve();
})
.catch(function(err) {
// handle error for Q.all()
reject();
})
})
.catch(function(err) {
// handle error for User.find()
reject();
});
});
}
But now I learnt that I can remove the nesting with something like:
function X() {
return Q.Promise(function(resolve, reject) {
return Users.find()
.then(function(user) {
return Q.all([
getUserProfileInfo(user),
getUserSomethingElse(user)
]);
})
.spread(function(profile, something) {
// do stuff
resolve();
})
.catch(function(err) {
// now how do I differenciate between
// the errors from Users.find() and Q.all()?
reject();
});
});
}
The "problem" I have with the bottom (flattened) version is how do I differenciate the errors from Users.find() and Q.all()? Or in general possibly many other errors in the long then chain?
Currently I might use something like
function(err) {
if (err.errors && err.statusCode) {
// do something
} else if (err.message) {
// do something
}
}
But this is not really "nice" code isit? There must be a better way?

You can use multiple catch blocks, like this
function X() {
return Q.Promise(function(resolve, reject) {
return Users.find()
.catch(function(err) {
// Handle user finding error
throw new Error("Problem in finding users");
})
.then(function(user) {
return Q.all([
getUserProfileInfo(user),
getUserSomethingElse(user)
]);
})
.spread(function(profile, something) {
// do stuff
resolve();
})
.catch(function(err) {
reject();
});
});
}
Now, if there is a problem in finding the user, the first catch block will be executed and since you are throwing an Error from that, it will reach the next catch block in the chain. If there is no error in the Users.find then control will directly go to the then block.

Related

How to handle reject promise error in outer try catch with inner Promise.all?

When an error/rejection occurs in detectingDog or detectingDog, the error is successfully handled by the .catch(error of the Promise.all() but I want the error to be directly handled by the catch (err) of the try structure.
How can I do this ?
PS: I have already tried to get rid of the .catch(error but then the Promise.all() hangs forever
try {
function detectingDog(bufferedData) {
return new Promise((resolve, reject) => {
package.detectDog(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
function detectingCat(bufferedData) {
return new Promise((resolve, reject) => {
package.detectCat(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
Promise.all([
detectingDog(param1),
detectingCat(param2)
]).then(responseData => {
callback(undefined, responseData);
}).catch(error => {
// (1) I need to pass the error to the outer structure where error handling is done
});
} catch (err) {
console.log(err);
// handing of the inner error (2) here
callback(err);
}
Thanks!
...but I want the error to be directly handled by the catch (err) of the try structure.
You can't do that in a non-async function, because control has already left the try/catch by the time that rejection occurs, which is after whatever function this code is in (if any) has returned.
In an async function, you can use await on a promise, which will make a rejection throw, so it would go to your try/catch. So you could do the following, but keep reading because it's fairly odd:
// In an `async` function
try {
function detectingDog(bufferedData) {
return new Promise((resolve, reject) => {
package.detectDog(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
function detectingCat(bufferedData) {
return new Promise((resolve, reject) => {
package.detectCat(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
return resolve(data);
}
});
});
}
const responseData = await Promise.all([
detectingDog(param1),
detectingCat(param2)
]);
callback(responseData);
} catch (err) {
console.log(err);
callback(err);
}
...but it doesn't make a lot of sense to go to the trouble of converting callback APIs to promises if you're just going to provide a callback-based API to your caller. Just return a promise. That makes the whole try/catch disappear:
// No need for these to be nested
function detectingDog(bufferedData) {
return new Promise((resolve, reject) => {
package.detectDog(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
resolve(data); // No need for `return`
}
});
});
}
function detectingCat(bufferedData) {
return new Promise((resolve, reject) => {
package.detectCat(bufferedData, function(error, data) {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
function example(param1, param2) {
return Promise.all([
detectingDog(param1),
detectingCat(param2)
]);
}
You have two options here.
If you really need the try/catch block you will need to run your code in an async function, leveraging the fact that awaiting a rejected Promise will throw an error in this context:
(async function () { // you might not need the immediately invoking function wrapper depending on your context
try {
function one(bufferedData) {
// return a promise
}
function two(bufferedData) {
// return a Promise
}
const responseData = await Promise.all([
one(param1),
two(param2)
])
callback(undefined, responseData)
} catch (err) {
console.log(err);
// handing of the inner error (2) here
callback(err)
}
})()
Alternatively, you can also just handle the error in the catch block of your Promise chain:
function one(bufferedData) {
// return a promise
}
function two(bufferedData) {
// return a Promise
}
Promise.all([
one(param1),
two(param2)
])
.then((responseData) => {
callback(undefined, responseData)
})
.catch((err) => {
console.log(err);
// handing of the inner error (2) here
callback(err)
})

Node.js: Unhandled promise rejection - UnhandledPromiseRejectionWarning

I have a Node.js app. This app has a button that starts a process. The steps in that process return promises. I'm trying to chain these promises together. For some reason, I'm receiving an UnhandledPromiseRejectionWarning. However, in my mind, I've set this up correctly. My code looks like this:
var myButton = document.getElementById('myButton');
if (myButton) {
console.log('here');
myButton.addEventListener('click', executeAction('0'));
}
function executeAction(input) {
let param1 = 'A';
let promise = new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
return promise;
}
function executeStep1() {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
resolve('[something]');
}, 3000);
} catch (ex) {
reject();
}
});
return promise;
}
function executeStep2(p1, p2, p3) {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
console.log('step 2 has executed');
resolve('awesome!')
}, 3000);
} catch (ex) {
reject(ex);
}
});
return promise;
}
I've confirmed that the executeStep2 function runs to completion. I'm basing this in the fact that I can see "step 2 has executed" in the console window. However, to my surprise, I never see "All done" printed in the console window. Instead, I see the UnhandledPromiseRejectionWarning mentioned above. I don't understand two things about this result:
Why am I not seeing "All done" in the console? Shouldn't that function get executed after executeStep2 has resolved?
Where is the rejection coming from? I don't see anything that's rejecting this.
Thank you very much for your help!
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => { // no function(result) here
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
The error is generated from when you call the function(s) that uses promises:
myButton.addEventListener('click', executeAction('0'));
You need to catch rejections there also.
myButton.addEventListener('click', executeAction('0')
.catch((error) => console.log('ERROR', error));
The rejections are caught inside the functions, but not in the outer scope because executeAction('0') returns a promise, or it would but you are using it as a non-async function, so its creating a promise and then returning a pending promise without waiting for it to be resolved. That looks like what's causing the rejection, and its also not handled for the above reason.
This will fix it:
function executeAction(input) {
let param1 = 'A';
return new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
}
You should look into async/await. It can clean this code up significantly.
async function getSomething() {
try {
// throw 'Test error detected.'
return 'test'
}
catch (e) {
throw e
}
}
async function testing() {
try {
const sample = await getSomething()
return sample
} catch (e) {
throw e
}
}
testing()
.then((data) => console.log(data))
.catch((err) => console.log(err))
Run this above example, and then uncomment the throw. Use this pattern for maximum winning. Exceptions should be thrown to the surface level where the functions are called, displayed, used, rendered, etc. Functions should simply throw the error out.
This allows you to use error handling middleware, higher-order functions, listeners, logging, etc.

Javascript promise: catch flow

In the following code, I want to be able to exit app when foo fails, but not when goo fails. My problem is that when goo rejects, it's getting caught in foo's catch. Is there a promise best practice solution to this issue?
foo().then(() => {
return goo.catch(() => {
//do something
//reject but don't exit app
})
})
.catch(() => {
//exit app
//reject
})
Don't reject in goo.catch - you don't want rejection to propagate to the foo.catch handler.
foo().then(() => {
return goo.catch(() => {
// do something but don't reject
})
})
.catch(() => {
// exit app
// reject
})
So you enter catch block and "recover" from error situation. By throwing/rejecting you pass exception further, in case of goo you don't want this.
Simply return from goo.catch() block (or call Promise.resolve())
const gooError;
foo().then(() => {
return goo.catch(err => {
// set the error thrown in to gooError that is
// declared before the promise chain
// and then simply 'return'
gooError = err;
return;
});
// to ensure goo error is taken outside immediately
// You can omit this if not required.
if (gooError) return;
})
.catch(() => {
// exit app
// reject
})
// check if there was any error by goo
if (gooError) {
// handle goo error here
}
I think this does the trick:
foo()
.catch(() => {
//exit app
//reject
})
.then(() => {
return goo.catch(() => {
//do something
//reject
})
})
Let's see what you want to achieve in synchronous code:
function main() {
try {
foo();
} catch(e) {
console.log("Exit process");
throw e;
}
try {
goo();
} catch(e) {
console.log("do something");
throw e;
}
}
You can achieve this by nesting catches:
function foo() {
return Promise.reject();
}
function goo() {
return Promise.reject();
}
function main() {
return foo().catch(e => {
console.log("Exit process");
// what would be point in rejecting if the process already exited anyway?
throw e;
}).then(() => {
return goo().catch(e => {
console.log("Do something");
throw e;
});
});
}
main().catch(() => { console.log("Main is rejected"); });

How to return different promises from a function?

A function whose declared type is neither 'void' nor 'any' must return a value.
I have a createOrder function, that is supposed to return a promise. I have database calls in the function, and I'd like to resolve the promise of the createOrder function, when all database actions have finished.
The problem is that the typescript compiler don't let me return the promise from inside the database function returned promise because it thinks that the function declaration (return a Promise) isn't fulfilled:
createOrder(order:Array<Cartitem>, sum:number):Promise<Array<Cartitem>> {
this.db.put(newOrder)
.then((r)=>{
//other operations
return new Promise<Array<Cartitem>>((resolve) => {
resolve(order);
});
})
.catch(function (err) {
console.log('put order:', err);
// return new Promise<Array<Cartitem>>((reject) => {
// reject(order);
// });
});
}
I can return a Promise at the end of the function, but that is not good for anything of course except for the compiler. Should I use a simple callback here?
Depending on what you're doing in "other operations," you either use the promise created by calling then on this.db.put's promise if you can:
createOrder(order:Array<Cartitem>, sum:number):Promise<Array<Cartitem>> {
return this.db.put(newOrder)
.then((r)=>{
//other operations
return order; // Resolves the one created by `then`
})
.catch(function (err) {
console.log('put order:', err);
throw err; // Rejects the one created by `then`
});
});
}
...or if you can't for some reason, create your promise earlier, and make your call to this.db.put inside its initialization callback:
createOrder(order:Array<Cartitem>, sum:number):Promise<Array<Cartitem>> {
return new Promise<Array<Cartitem>>((resolve) => {
this.db.put(newOrder)
.then((r)=>{
//other operations
resolve(order);
})
.catch(function (err) {
console.log('put order:', err);
reject(err); // You had `order` here, but presumably it would be `err`
});
});
}
You can return the Promise as your function's return value, and resolve it in your inner promise callbacks:
createOrder(order:Array<Cartitem>, sum:number):Promise<Array<Cartitem>>
{
return new Promise<Array<Cartitem>>((resolve) => {
this.db.put(newOrder)
.then((r)=>{
//other operations
resolve(order);
})
.catch((err) => {
console.log('put order:', err);
reject(err);
});
});
}
try something like
createOrder(order:Array<Cartitem>, sum:number):Promise<Array<Cartitem>> {
return this.db.put(newOrder)
.then((r)=>{
//other operations
return new Promise<Array<Cartitem>>((resolve) => {
resolve(order);
});
})
.catch(function (err) {
console.log('put order:', err);
// return new Promise<Array<Cartitem>>((reject) => {
// reject(order);
// });
return null
});
}
You should actually add the return of this.db.put (and the whole expression following it).
As it stands, you are invoking this.db.put, and chaining a callback in the then branch, but you aren't returning anything to the caller. Subsequently, callers might expect to be able to .then off of the result of createOrder, but in plain-old JavaScript, it will fail, since the result will be undefined.
You can also just resolve(order) in .then, rather than return a Promise of array etc.

How to reject a promise from inside then function

This is probably a silly question, but mid promise chain, how do you reject a promise from inside one of the then functions? For example:
someActionThatReturnsAPromise()
.then(function(resource) {
return modifyResource(resource)
})
.then(function(modifiedResource) {
if (!isValid(modifiedResource)) {
var validationError = getValidationError(modifiedResource);
// fail promise with validationError
}
})
.catch(function() {
// oh noes
});
There's no longer a reference to the original resolve/reject function or the PromiseResolver. Am I just supposed to add return Promise.reject(validationError); ?
Am I just supposed to add return Promise.reject(validationError);?
Yes. However, it's that complicated only in jQuery, with a Promise/A+-compliant library you also could simply
throw validationError;
So your code would then look like
someActionThatReturnsAPromise()
.then(modifyResource)
.then(function(modifiedResource) {
if (!isValid(modifiedResource))
throw getValidationError(modifiedResource);
// else !
return modifiedResource;
})
.catch(function() {
// oh noes
});
Use Promise.reject
Like this
function test()
{
return new Promise((resolve, reject) => {
resolve(null)
}).then(() => console.info('yes')).then(() => {
return Promise.reject('hek');
})
}
test().then(() => console.info('resolved')).catch(() => console.info('rejected'))

Categories