I need to write a function that returns a promise, where first I call a synchronous function A() which returns some result.
Then return a function B(result) where B is a promise which takes in the result of A().
If either function fails I want the same error function C(error) to get called where C is a promise.
What is the best way of writing this. This is what I have but think there is obvious way I am missing
function() {
try {
var result = A();
return B(result)
.catch(function(error) {
return C(error);
});
}
catch(error) {
return C(error);
}
}
It seems wrong combining synchronous try and catch with a promise .catch and also wrong there are two different places I need to call C(error).
A() throws an error rather than returning an error code.
You don't say exactly how A()
fails. It could either throw or it could return an error result. I'll show a scheme for both. The key to a mix of sync and async is to always return a promise. This will give you a consistent interface for teh caller no matter how the function succeeds or fails.
If you are only worried about A()
throwing an exception and it doesn't return an error code, then you can do this:
function someFunction() {
try {
var result = A();
return B(result);
} catch(err) {
return Promise.reject(err);
}
}
someFunction().then(function(result) {
// code here to process the final result
}).catch(C);
If you also have the case where A()
can return an error code, then you can do this:
function someFunction() {
try {
var result = A();
// check for error value
if (result < 0) {
throw result;
}
return B(result);
} catch(err) {
return Promise.resolve(err);
}
}
Note that both of these patterns avoid creating an extra promise if it isn't needed. They only create the extra promise when returning an error that occurred synchronously.
The Bluebird promise library has a helper function for this particular circumstance called Promise.method
. The utility of Promise.method()
is that it automatically wraps your function in a try/catch handler and if there are any synchronous exceptions thrown, it automatically turns them into returning a rejected promise. You could use it like this:
var someFunction = Promise.method(function() {
var result = A();
// check for error condition
if (result < 0) {
throw result;
}
return B(result);
});
someFunction().then(function(result) {
// code here to process the final result
}).catch(C);
I'm assuming that both A
and B
can throw errors here. Using the standard API it could look like this:
function() {
return new Promise((resolve, reject) => {
try {
resolve(A());
} catch (error) {
reject(error);
}
})
.then(B)
.catch(C);
}
This will return a promise that's either resolved with the output of B
or the output of C
, if that provides a fallback. You can also consider handling any errors outside of this function if that makes sense for your use case.
When using Bluebird this should also be possible:
function() {
return Promise.method(A)().then(B).catch(C)
}
I think a good way to do this would be to still use promises for the synchronous function. It keeps it consistent within a function, especially if you want something to respond to the success like a pseudo-promise. But the key is that you'd use an immediately resolved promise. Take a look at this blog post on ES6 Promises.
// an immediately resolved promise
var a = Promise.resolve(A());
assuming you've already created the promise and defined C like so:
var B = new Promise(function(resolve, reject) {
if (a) {
resolve('success'); // fulfilled successfully
}
else {
C('rejected'); // error, rejected
}
})
.then((result) => {console.log('made it!');})
.catch((result) => {C('rejected');});
var C = (err)=>{console.log('error: ' + err); return err;}
this code should do what you want:
a.then((result) => B(result));
^ this last line is the most important, since it uses the output for A to call B