I am writing a library in nodejs, that is wrapping another library. my code is something like this:
function wrapper(functionToWrap) {
return function(param1, param2) {
try {
return functionToWrap(param1, param2);
} catch(err) {
console.log(err);
throw err;
} finally {
finalizeWrapping();
}
}
}
the problem is that my finalizeWrapping function is a function that waits for promises that i collect (by placing some hooks before calling functionToWrap on some of the async apis it uses) to resolve, and only then acts, something like this:
function finalizeWrapping() {
Promise.all(pendingPromises).then(function(values) {
//finalize the wrapping
console.log('all promises fulfilled!');
});
}
the issue is that the error is thrown and node exits (this error should not be handled, since the wrapped function doesn't handle it) before all the promises are resolved and the then block is executed.
my question is: is there anything i can do to work around this, meaning throwing the error to the user appropriately and finish executing the then block, or do i have to change the way i hook the apis to be synchronous and not use a promise?
Thanks in advance to all the helpers :)
EDIT: an attempt to make my question clearer - functionToWrap is not my function, it is a function of a different library (and it can change - meaning i want my code to be able to wrap as many functions as possible). this function is allowed to use async apis (which i may be trying to monkeypatch), and basically it should have as least restrictions as possible - i want the user to be able to write any function and me being able to wrap it.
Not sure if the following can help, you may not have enough reputation to comment although I think you can comment on your own question and it's answers.
const wrapper = functionToWrap =>
function() {
//special type to indicate failed call to functionToWrap
const Fail = function(reason){this.reason=reason;};
//does not matter how many argument. Assuming functionToWrap
// does not rely on "this". If it does then also pass the
// object that functionToWrap is on and replace null with that object
return Promise.resolve(Array.from(arguments))
.then(
//if functionToWrap is on an object pass it to wrapper
// and replace null with that object
args=>functionToWrap.apply(null,args)
)
.catch(
//if functionToWrap throws an error or rejects we will catch it here
// and resolve with a special Fail type value
err=>{
console.log(err);
return new Fail(err)
}
).then(
//since promise cannot fail (its rejection is caught and resolves with Fail)
// this will always be called
//finalize should return Promise.all... and maybe not use a shared
// variable called pendingPromises, global shared mutable variables in async
// functions is asking for trouble
result=>finalizeWrapping().then(
()=>
//if functionToWrap rejected or thew the result will be a Fail type
(result && result.constructor === Fail)
? Promise.reject(result.reason)//reject with the original error
: result//resolve with the functionToWrap result
)
);
}
Related
I am working on a simple project and I would like to create a simple helper function that checks for a error in a callback. If there is a error then it should break the whole function that called it. Code example:
//Makes call to database and tries to insert element
db.collection("data").insertOne(
{
key: 'some-data'
}, (error, result) => {
//Return error if something goes wrong - else error is empty
checkError(error, "Unable to load database");
console.log("Succes item added")
}
);
Note: Yes this is node.js but this whole principle could be repeated in js with other callbacks - very simple repeatable error principle.
So in the insertOne function the first argument is some data I am adding to the database. The second argument is the callback function that is called after this async operation is finished. It returns a error which I could just handle by adding this if statement to the callback:
if (error) {
console.error(error);
return;
}
Buuut thats disrespecting the dry principle (bc I write the exact same if statement everywhere with no syntax being changed except the message) and is also distracting when reading the callback function. Now my issue is in the function checkError() even tho I can just print the error with the message or throw the error, I dont actually have a way to break the original callback so that it doesnt cause any more havoc in my database. I will go on to promisify this callback which is a solution. BUT I want to know if there is a way to this in the way I presented it here. Note: I dont want to use the try catch block bc thats replacing a if statement with another two blocks.
My checkError function:
const checkError = function (error, msg = "Something went wrong") {
if (error) console.error(`${msg}: error`);
//Break original block somehow ¯\_(ツ)_/¯
};
If I were to compress my question it would be: how to break a function with another function. Is there any way to achieve this?
I don't think this is possible. But you could achieve something similar with this:
function checkError (error, msg = "Something went wrong") {
if (!error) return false;
console.error(`${msg}: error`);
return true;
};
db.collection("data").insertOne(
{
key: 'some-data'
}, (error, result) => {
//Return error if something goes wrong - else error is empty
if (checkError(error, "Unable to load database")) return;
console.log("Succes item added")
}
);
Things become easier when you use promises.
Often asynchronous APIs provide a promise interface, and this is also the case for mongodb/mongoose, where you can chain a .exec() call to execute the database query and get a promise in return. This gives you access to the power of JavaScript's async/await syntax. So you can then do like this:
async function main() {
// Connect to database
// ...
// Other db transactions
// ...
let result = await db.collection("data").insertOne({ key: 'some-data'}).exec();
console.log("Item added successfully");
// Any other database actions can follow here using the same pattern
// ...
}
main().catch(err => {
console.log(err);
});
The idea here is that await will throw an exception if the promise returned by .exec() eventually rejects. You can either put a standard try...catch construct around it to deal with that error, or you can just let it happen. In the latter case the promise returned by the wrapping async function will reject. So you can deal with the error at a higher level (like done above).
This way of working also removes the need for numerous nested callbacks. Often you can keep the nesting to just one of two levels by using promises.
I have a nodeJS project where I wish to use asynchronous functions. Namely, I'm using the Q library.
I have a function called someFunction(), that I wish to return a promise. With the then -function, I can then check whether the promise was resolved or rejected like so:
someFunction()
.then(
function(results) {
console.log("Success!");
},
function (error) {
console.log("An error occurred, and I would wish to log it for example");
}
);
What I intuitively expect with the function above is, that the error function would catch all possible errors. So if some exception is raised inside the someFunction, I can rest assured, that the error function will be run (the second function after 'then'). But it seems this is not the case.
For example, let's say the someFunction would be defined as so:
function someFunction() {
var deferred = Q.defer();
throw new Error("Can't bar.");
deferred.resolve("success");
}
Now if I call the function someFunction() like done in the upper code block, it won't run the error function. Why is that? Isn't the partial point of the promise Q.deferred to catch errors? Why should I manually reject every error that happens? I know I could set the whole content of someFunction to try/catch clause, and then reject the deferred, but that feels so wrong! There must be a better way, and I know for sure some of you stackoverflowers know it!
With this information, I began to think about where the deferred.reject and deferred.resolve is even meant to be used? Is it even meant to catch exceptions? Should I really just go through all the error cases manually, and call the deferred.reject on them? I'm interested to hear how this should be handled professionally.
Q has specific function for success and error, so use:
deferred.reject("error");
intead of throwing an Exception.
Next thing is that someFunction must return promise to be used as You use it:
function someFunction() {
var deferred = Q.defer();
try{
//some Asynchronous code
deferred.resolve("success");
}catch(e){
deferred.reject(e.message);
}
return deffered.promise; //return promise to use then
}
Because Promises ain't magic. They don't somehow magically catch Errors. They catch them, because they wrap the calls to the callbacks in try..catch-blocks, to convert Errors into rejected Promises.
If you want an Error to be handled by the Promise-chain, well put the function-call into a promise-chain: Q.resolve().then(someFunction).then(...).
Now any synchronous Error occuring in someFunction can be handled in the following then's.
Btw: If you use Q.defer(), and you're not dealing with some callback-style API, you're doing it definitely wrong. Search for the Deferred-antipattern.
it won't run the error function. Why is that?
Because you synchronously threw an exception, instead of returning a promise. Which you never should.
Isn't the partial point of the promise Q.deferred to catch errors?
No. then and catch implicitly catch exceptions in their callbacks, defferreds don't - they're just a (deprecated) API to create promises.
Why should I manually reject every error that happens?
Because asynchronous errors are expected to be passed to callbacks anyway, instead of being thrown.
I know I could set the whole content of someFunction to try/catch clause, and then reject the deferred, but that feels so wrong! There must be a better way!
There is: the Q.Promise constructor, the standard (ES6) promise creation API. It has the benefit of being able to catch synchronous exceptions from the executor function:
function someFunction() {
return new Q.Promise(function(resolve) {
throw new Error("Can't bar.");
resolve("success");
});
}
throwing an error will stop the code from executing (and will close node) unless you catch the error in a try/catch block.
handled errors from requests can be passed to the .catch chain by using deferred.reject(error). code errors and custom thrown errors needs to be handled inside try/catch, which is the right way to handle such errors.
function someFunction() {
var deferred = Q.defer();
deferred.reject("Can't bar.");
// or
try {
throw new Error("Can't bar.");
}
catch(err) {
deferred.reject("Can't bar.");
}
deferred.resolve("success");
}
-- EDIT --
I encountered a weird thing recently about promises, but I guess it's maybe because it's against the philosophy of promises.
Considering the following code :
// Assuming Auth is just a simple lib doing http requests with promises
Auth.signup()
.then(succCall, errCall)
.then(loginSucc, loginErr)
// My callbacks here
function succCall (){
// OK, send second promise
console.log('succCall');
return Auth.login();
}
function errCall(){
// I do some things here and now
// I want to break out from here
console.log('errCall');
}
function loginSucc(){
// This is the callback of the login method when it went OK
// I want to enter here ONLY if with go through the succCall
console.log('loginSucc');
}
function loginErr(){
// This is the callback of the login method when it went not ok
// I want to enter here ONLY if with go through the succCall
console.log('loginErr');
}
Here if something goes wrong in Auth.signup(), this is what show :
errCall, loginSucc
if i do a $q.reject() in the errCall this is what happens :
errCall, loginErr
and this is what i want :
errCall... finish, stop here
Now, the problem is, it goes in errCall when signup goes wrong, that's good, but then it enters loginSucc...
I want to break out of the then chain when any errorCallback (which is errCall or loginErr here) is encountered.
-- EDIT --
I think i was misunderstood by some mean, i want to totally break the chain without check in any other "then" if something went wrong.
As if i was saying : if first then is wrong stop here, if first then ok continue, if second "then" ok continue, if third "then" wrong, stop
// Just like if i did the following but by chainning "then" methods
// My callbacks here
function succCall (){
// OK, send second promise
return Auth.login().then(loginSucc, loginErr);
}
My point is, i don't want only one error handler if i have many "then" chained
What is effectively happening is this:
try {
try {
var a = succCall();
} catch(e1) {
a = errCall(e1);
}
var b = loginSucc(a);
} catch(e2) {
b = loginErr(e2);
}
You can break out of the chain by calling
return $q.reject('Reason Err was called');
in your errCall() function.
EDIT:
As OP remarked by calling $q.reject the code will enter the loginErr function.
Alternatively you can modify your code like this:
Auth.signup()
.then(function(a) {
succCall()
return loginSucc(a).then(null, loginErr);
}, errCall)
You can read more in these two SO question:
Break promise chain
Break Out of then promises in
Angularjs
This also is a helpful read : Flattening Promise Chains
errCall function needs tor return a promise, and that promise needs to be rejected for loginErr to be fired.
function errCall(){
// i do some things here and now
return $q(function(resolve, reject) {
// auto reject
reject();
});
}
Alternatively try .catch:
Auth.signup()
.then(succCall)
.then(loginSucc)
.catch(function(err){
// caught error, problem is you won't know which function errored out, so you'll need to look at the error response
});
Just don't pass any errCall or loginErr to then and use catch() in the end of the promise chain and it will be interrupted on first error, which will be passed to catch(). If you want explicitly process error of Auth.signup() then your errCall should look like this:
function (err) {
if(isFatal(err)) {
return Promise.reject(new Error('Fatal!')); //`catch()` handler will be called with `new Error('Fatal!')`
} else {
return 'something'; //next `then()` handler will be called with 'something'
}
}
Your best option is to return a promise that is never resolved from errCall():
function errCall() {
console.log('errCall');
return $q.defer().promise;
}
But you are right, what you are trying to do is "against the philosophy of promises".
Why you shouldn't do it
It is of course a problem that your loginSucc function is called when an error occurs during evaluation of the first promise. However, that can be fixed by rejecting once again in errCall as others have already pointed out. But you should not try to avoid the execution of loginErr, otherwise something is conceptually wrong with your code.
The evaluation of the expression
Auth.signup().then(succCall, errCall)
yields another promise. The contract is that a promise has a then() method taking two callbacks as parameters, one for success and one for failure. The contract is also that the success callback is called when the promise is evaluated successfully and that the error/failure callback is called in all other cases. If you intend to never call either of those, don't use a promise!
There are many tutorials on how to use "then" and "catch" while programming with JavaScript Promise. However, all these tutorials seem to miss an important point: returning from a then/catch block to break the Promise chain. Let's start with some synchronous code to illustrate this problem:
try {
someFunction();
} catch (err) {
if (!(err instanceof MyCustomError))
return -1;
}
someOtherFunction();
In essence, I am testing a caught error and if it's not the error I expect I will return to the caller otherwise the program continues. However, this logic will not work with Promise:
Promise.resolve(someFunction).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
return -1;
}
}).then(someOtherFunction);
This logic is used for some of my unit tests where I want a function to fail in a certain way. Even if I change the catch to a then block I am still not able to break a series of chained Promises because whatever is returned from the then/catch block will become a Promise that propagates along the chain.
I wonder if Promise is able to achieve this logic; if not, why? It's very strange to me that a Promise chain can never be broken. Thanks!
Edit on 08/16/2015:
According to the answers given so far, a rejected Promise returned by the then block will propagate through the Promise chain and skip all subsequent then blocks until is is caught (handled). This behavior is well understood because it simply mimics the following synchronous code (approach 1):
try {
Function1();
Function2();
Function3();
Function4();
} catch (err) {
// Assuming this err is thrown in Function1; Function2, Function3 and Function4 will not be executed
console.log(err);
}
However, what I was asking is the following scenario in synchronous code (approach 2):
try {
Function1();
} catch(err) {
console.log(err); // Function1's error
return -1; // return immediately
}
try {
Function2();
} catch(err) {
console.log(err);
}
try {
Function3();
} catch(err) {
console.log(err);
}
try {
Function4();
} catch(err) {
console.log(err);
}
I would like to deal with errors raised in different functions differently. It's possible that I catch all the errors in one catch block as illustrated in approach 1. But that way I have to make a big switch statement inside the catch block to differentiate different errors; moreover, if the errors thrown by different functions do not have a common switchable attribute I won't be able to use the switch statement at all; under such a situation, I have to use a separate try/catch block for each function call. Approach 2 sometimes is the only option. Does Promise not support this approach with its then/catch statement?
This can't be achieved with features of the language. However, pattern-based solutions are available.
Here are two solutions.
Rethrow previous error
This pattern is basically sound ...
Promise.resolve()
.then(Function1).catch(errorHandler1)
.then(Function2).catch(errorHandler2)
.then(Function3).catch(errorHandler3)
.then(Function4).catch(errorHandler4)
.catch(finalErrorHandler);
Promise.resolve() is not strictly necessary but allows all the .then().catch() lines to be of the same pattern, and the whole expression is easier on the eye.
... but :
if an errorHandler returns a result, then the chain will progress to the next line's success handler.
if an errorHandler throws, then the chain will progress to the next line's error handler.
The desired jump out of the chain won't happen unless the error handlers are written such that they can distinguish between a previously thrown error and a freshly thrown error. For example :
function errorHandler1(error) {
if (error instanceof MyCustomError) { // <<<<<<< test for previously thrown error
throw error;
} else {
// do errorHandler1 stuff then
// return a result or
// throw new MyCustomError() or
// throw new Error(), new RangeError() etc. or some other type of custom error.
}
}
Now :
if an errorHandler returns a result, then the chain will progress to the next FunctionN.
if an errorHandler throws a MyCustomError, then it will be repeatedly rethrown down the chain and caught by the first error handler that does not conform to the if(error instanceof MyCustomError) protocol (eg a final .catch()).
if an errorHandler throws any other type of error, then the chain will progress to the next catch.
This pattern would be useful if you need the flexibility to skip to end of chain or not, depending on the type of error thrown. Rare circumstances I expect.
DEMO
Insulated Catches
Another solution is to introduce a mechanism to keep each .catch(errorHandlerN) "insulated" such that it will catch only errors arising from its corresponding FunctionN, not from any preceding errors.
This can be achieved by having in the main chain only success handlers, each comprising an anonymous function containing a subchain.
Promise.resolve()
.then(function() { return Function1().catch(errorHandler1); })
.then(function() { return Function2().catch(errorHandler2); })
.then(function() { return Function3().catch(errorHandler3); })
.then(function() { return Function4().catch(errorHandler4); })
.catch(finalErrorHandler);
Here Promise.resolve() plays an important role. Without it, Function1().catch(errorHandler1) would be in the main chain the catch() would not be insulated from the main chain.
Now,
if an errorHandler returns a result, then the chain will progress to the next line.
if an errorHandler throws anything it likes, then the chain will progress directly to the finalErrorHandler.
Use this pattern if you want always to skip to the end of chain regardless of the type of error thrown. A custom error constructor is not required and the error handlers do not need to be written in a special way.
DEMO
Usage cases
Which pattern to choose will determined by the considerations already given but also possibly by the nature of your project team.
One-person team - you write everything and understand the issues - if you are free to choose, then run with your personal preference.
Multi-person team - one person writes the master chain and various others write the functions and their error handlers - if you can, opt for Insulated Catches - with everything under control of the master chain, you don't need to enforce the discipline of writing the error handlers in that certain way.
First off, I see a common mistake in this section of code that could be completely confusing you. This is your sample code block:
Promise.resolve(someFunction()).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
return -1;
}
}).then(someOtherFunction()); // <== Issue here
You need pass function references to a .then() handler, not actually call the function and pass their return result. So, this above code should probably be this:
Promise.resolve(someFunction()).then(function() {
console.log('someFunction should throw error');
return -2;
}).catch(function(err) {
if (err instanceof MyCustomError) {
// returning a normal value here will take care of the rejection
// and continue subsequent processing
return -1;
}
}).then(someOtherFunction); // just pass function reference here
Note that I've removed () after the functions in the .then() handler so you are just passing the function reference, not immediately calling the function. This will allow the promise infrastructure to decide whether to call the promise in the future or not. If you were making this mistake, it will totally throw you off for how the promises are working because things will get called regardless.
Three simple rules about catching rejections.
If nobody catches the rejection, it stops the promise chain immediately and the original rejection becomes the final state of the promise. No subsequent handlers are invoked.
If the promise rejection is caught and either nothing is returned or any normal value is returned from the reject handler, then the reject is considered handled and the promise chain continues and subsequent handlers are invoked. Whatever you return from the reject handler becomes the current value of the promise and it as if the reject never happened (except this level of resolve handler was not called - the reject handler was called instead).
If the promise reject is caught and you either throw an error from the reject handler or you return a rejected promise, then all resolve handlers are skipped until the next reject handler in the chain. If there are no reject handlers, then the promise chain is stopped and the newly minted error becomes the final state of the promise.
You can see a couple examples in this jsFiddle where it shows three situations:
Returning a regular value from a reject handler, causes the next .then() resolve handler to be called (e.g. normal processing continues),
Throwing in a reject handler causes normal resolve processing to stop and all resolve handlers are skipped until you get to a reject handler or the end of the chain. This is effective way to stop the chain if an unexpected error is found in a resolve handler (which I think is your question).
Not having a reject handler present causes normal resolve processing to stop and all resolve handlers are skipped until you get to a reject handler or the end of the chain.
There is no built-in functionality to skip the entirety of the remaining chain as you're requesting. However, you could imitate this behavior by throwing a certain error through each catch:
doSomething()
.then(func1).catch(handleError)
.then(func2).catch(handleError)
.then(func3).catch(handleError);
function handleError(reason) {
if (reason instanceof criticalError) {
throw reason;
}
console.info(reason);
}
If any of the catch blocks caught a criticalError they would skip straight to the end and throw the error. Any other error would be console logged and before continuing to the next .then block.
If you can use the newer async await this is pretty simple to implement:
async function myfunc() {
try {
return await anotherAsyncFunction();
} catch {
//do error handling
// can be async or not.
return errorObjct();
}
}
let alwaysGetAValue = await myfunc();
Depending on what technology your using you may need some kind of high level wrapper function to allow for the top level await.
Using the https://github.com/kriskowal/q library, I'm wondering if it's possible to do something like this:
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
}
});
}
// Module B
moduleA_exportedFunction()
.then(moduleB_function)
.then(moduleB_anotherFunction)
.fail(function(reason) {
// Handle the reason in a general way which is ok for module B functions
})
.done()
;
Basically if the service results are bad, I'd like to handle the failure in module A, using logic that is specific to the internals of module A, but still skip the remaining module B functions in the promise chain.
The obvious solution for skipping module B functions is to throw an error/reason from module A. However, then I would need to handle that in module B. And ideally I'd like to do it without needing any extra code in module B for that.
Which may very well be impossible :) Or against some design principles of Q.
In which case, what kind of alternatives would you suggest?
I have two approaches in mind, but both have their disadvantages:
Throw a specific error from module A and add specific handling code to module B:
.fail(function(reason) {
if (reason is specificError) {
performVerySpecificErrorHandling();
}
else {
// Handle the reason in a general way which is ok for module B functions
}
})
Perform the custom error handling in module A, then after handling the error, throw a fake rejection reason. In module B, add a condition to ignore the fake reason:
.fail(function(reason) {
if (reason is fakeReason) {
// Skip handling
}
else {
// Handle the reason in a general way which is ok for module B functions
}
})
Solution 1 requires adding module A specific code to module B.
Solution 2 solves this, but the whole fake rejection approach seems very hackish.
Can you recommend other solutions?
Let's talk about control constructs.
In JavaScript, code flows in two ways when you call a function.
It can return a value to the caller, indicating that it completed successfully.
It can throw an error to the caller, indicating that an exceptional operation occurred.
It looks something like:
function doSomething(){ // every function ever
if(somethingBad) throw new Error("Error operating");
return value; // successful completion.
}
try{
doSomething();
console.log("Success");
} catch (e){
console.log("Boo");
}
Promises model this exact same behavior.
In Promises, code flows in exactly two ways when you call a function in a .then handler:
It can return a promise or a value which indicates that it completed successfully.
It can throw an error which indicates that an exceptional state occured.
It looks something like:
var doSomething = Promise.method(function(){
if(somethingBad) throw new Error("Error operating");
return someEventualValue(); // a direct value works here too
}); // See note, in Q you'd return Q.reject()
Promise.try(function(){ // in Q that's Q().then
doSomething();
console.log("Success");
}).catch(function(e){
console.log("Boo");
});
Promises model flow of control itself
A promise is an abstraction over the notion sequencing operations itself. It describes how control passes from one statement from another. You can consider .then an abstraction over a semicolon.
Let's talk about synchronous code
Let's see how synchronous code would look in your case.
function moduleA_exportedFunction() {
var serviceResults = someSynchronousFunction();
if (serviceResults.areGood) {
// We can continue with the rest of our code
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the chain
}
}
So, how continuing with the rest of our code is simply returning. This is the same in synchronous code and in asynchronous code with promises. Performing very specific error handling is also ok.
How would we skip the rest of the code in the synchronous version?
doA();
doB();
doC(); // make doD never execute and not throw an exception
doD();
Well, even if not immediately, there is a rather simple way to make doD never execute by causing doC to enter into an infinite loop:
function doC() {
if (!results.areGood) {
while(true){} // an infinite loop is the synchronous analogy of not continuing
// a promise chain.
}
}
So, it is possible to never resolve a promise - like the other answer suggests - return a pending promise. However, that is extremely poor flow control since the intent is poorly conveyed to the consumer and it will be likely very hard to debug. Imagine the following API:
moduleA_exportedFunction - this function makes an API request and returns the service as a ServiceData object if the data is available. Otherwise, it enters the program into an endless loop.
A bit confusing, isn't it :)? However, it actually exists in some places. It's not uncommon to find the following in really old APIs.
some_bad_c_api() - this function foos a bar, on failure it terminates the process.
So, what bothers us about terminating the process in that API anyway?
It's all about responsibility.
It's the responsibility of the called API to convey whether the API request was successful.
It is the responsibility of the caller to decide what to do in each case.
In your case. ModelA is simply breaching the limit of its responsibility, it should not be entitled to make such decisions about the flow of the program. Whoever consumes it should make these decisions.
Throw
The better solution is to throw an error and let the consumer handle it. I'll use Bluebird promises here since they're not only two orders of magnitude faster and have a much more modern API - they also have much much better debugging facilities - in this case - sugar for conditional catches and better stack traces:
moduleA_exportedFunction().then(function(result){
// this will only be reached if no error occured
return someOtherApiCall();
}).then(function(result2){
// this will be called if the above function returned a value that is not a
// rejected promise, you can keep processing here
}).catch(ApiError,function(e){
// an error that is instanceof ApiError will reach here, you can handler all API
// errors from the above `then`s in here. Subclass errors
}).catch(NetworkError,function(e){
// here, let's handle network errors and not `ApiError`s, since we want to handle
// those differently
}).then(function(){
// here we recovered, code that went into an ApiError or NetworkError (assuming
// those catch handlers did not throw) will reach this point.
// Other errors will _still_ not run, we recovered successfully
}).then(function(){
throw new Error(); // unless we explicitly add a `.catch` with no type or with
// an `Error` type, no code in this chain will run anyway.
});
So in a line - you would do what you would do in synchronous code, as is usually the case with promises.
Note Promise.method is just a convenience function Bluebird has for wrapping functions, I just hate synchronous throwing in promise returning APIs as it creates major breakage.
It is kind of a design thing. In general, when a module or service returns a promise, you want it to resolve if the call was successful, and to fail otherwise. Having the promise neither resolve or fail, even though you know the call was unsuccessful, is basically a silent failure.
But hey, I don't know the specifics of your modules, or reasons, so if you do want to fail silently in this case, you can do it by returning an unresolved promise:
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
return q.defer().promise;
}
});
}
Inspired by Benjamin Gruenbaum's comments and answer - if I was writing this in synchronous code, I would make moduleA_exportedFunction return a shouldContinue boolean.
So with promises, it would basically be something like this (disclaimer: this is psuedo-code-ish and untested)
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
return true;
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
return false;
}
});
}
// Module B
moduleA_exportedFunction()
.then(function(shouldContinue) {
if (shouldContinue) {
return moduleB_promiseReturningFunction().then(moduleB_anotherFunction);
}
})
.fail(function(reason) {
// Handle the reason in a general way which is ok for module B functions
// (And anything unhandled from module A would still get caught here)
})
.done()
;
It does require some handling code in module B, but the logic is neither specific to module A's internals nor does it involve throwing and ignoring fake errors - mission accomplished! :)