promises chain result if nothing rejected - javascript

I have a chain of promises, where I use catch to catch errors.
this.load()
.then(self.initialize)
.then(self.close)
.catch(function(error){
//error-handling
})
What function gets called if the chain has finished without rejection?
I was using finally, but this gets called also if an error occurs.
I'd like to call a function after the catch-function which gets only called if no promise was rejected.
I'm using node.js with the q - module.

Adding another then will do.
this.load()
.then(self.initialize)
.then(self.close)
.then(function() {
//Will be called if nothing is rejected
//for sending response or so
})
.catch(function(error){
//error-handling
})

I would change your .catch() to be a .then() and supply both an onFullfill and an onRejected handler. Then you can determine exactly which one happened and your code is very clear that one or the other will execute.
this.load()
.then(self.initialize)
.then(self.close)
.then(function() {
// success handling
}, function(error){
//error-handling
});
FYI, this isn't the only way to do things. You could also use .then(fn1).catch(fn2) which would similarly call fn1 or fn2 based on what the promise state was prior except that both could get called if fn1 returned a rejected promise or threw an exception because that would also be handled by fn2.

Related

How to handle async errors correctly?

When making a GraphQL query, and the query fails, Apollo solves this by having a data-object and an error-object.
When an async error is happening, we get the same functionality with one data-object and one error-object. But, this time we get an UnhandledPromiseRejectionWarning too, with information about: DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code..
So, we obviously need to solve this, but we want our async-functions to cast errors all the way up to Apollo. Do we need to try...catch all functions and just pass our error further up the tree? Coming from C#, were an exception just goes all the way to the top if never caught, it sounds like a tedious job to tell Apollo GraphQL that one (or more) leaves failed to retrieve data from the database.
Is there a better way to solve this, or is there any way to tell javascript/node that an uncaught error should be passed further up the call tree, until it's caught?
If you correctly chain your promises, you should never see this warning and all of your errors will be caught by GraphQL. Assume we have these two functions that return a Promise, the latter of which always rejects:
async function doSomething() {
return
}
async function alwaysReject() {
return Promise.reject(new Error('Oh no!'))
}
First, some correct examples:
someField: async () => {
await alwaysReject()
await doSomething()
},
// Or without async/await syntax
someField: () => {
return alwaysReject()
.then(() => {
return doSomething()
})
// or...
return alwaysReject().then(doSomething)
},
In all of these cases, you'll see the error inside the errors array and no warning in your console. We could reverse the order of the functions (calling doSomething first) and this would still be the case.
Now, let's break our code:
someField: async () => {
alwaysReject()
await doSomething()
},
someField: () => {
alwaysReject() // <-- Note the missing return
.then(() => {
return doSomething()
})
},
In these examples, we're firing off the function, but we're not awaiting the returned Promise. That means execution of our resolver continues. If the unawaited Promise resolves, there's nothing we can do with its result -- if it rejects, there's nothing we can do about the error (it's unhandled, as the warning indicates).
In general, you should always ensure your Promises are chained correctly as shown above. This is significantly easier to do with async/await syntax, since it's exceptionally easy to miss a return without it.
What about side effects?
There may be functions that return a Promise that you want to run, but don't want to pause your resolver's execution for. Whether the Promise resolves or returns is irrelevant to what your resolver returns, you just need it to run. In these cases, we just need a catch to handle the promise being rejected:
someField: async () => {
alwaysReject()
.catch((error) => {
// Do something with the error
})
await doSomething()
},
Here, we call alwaysReject and execution continues onto doSomething. If alwaysReject eventually rejects, the error will be caught and no warning will be shown in the console.
Note: These "side effects" are not awaited, meaning GraphQL execution will continue and could very well finish while they are still running. There's no way to include errors from side effects inside your GraphQL response (i.e. the errors array), at best you can just log them. If you want a particular Promise's rejection reason to show up in the response, you need to await it inside your resolver instead of treating it like a side effect.
A final word on try/catch and catch
When dealing with Promises, we often see errors caught after our function call, for example:
try {
await doSomething()
} catch (error) {
// handle error
}
return doSomething.catch((error) => {
//handle error
})
This is important inside a synchronous context (for example, when building a REST api with express). Failing to catch rejected promises will result in the familiar UnhandledPromiseRejectionWarning. However, because GraphQL's execution layer effectively functions as one giant try/catch, it's not really necessary to catch your errors as long as your Promises are chained/awaited properly. This is true unless A) you're dealing with side effects as already illustrated, or B) you want to prevent the error from bubbling up:
try {
// execution halts because we await
await alwaysReject()
catch (error) {
// error is caught, so execution will continue (unless I throw the error)
// because the resolver itself doesn't reject, the error won't be bubbled up
}
await doSomething()

Is there any way to check error in each Promise in promise.all when there is a promise error

I have one array of promises that each one execute a code that can have a javascript error and break the script. I need to check for each promise and catch any error. The problem is that the promise has timeout functions inside. Is there anyway to solve this?
Example code:
function apiRequest(url,customError) {
return new Promise(function (resolve, reject) {
if(customError) {
setTimeout(() => {
//Force error
var ar = url.asdasd.eadasd;
ar = ar.split('123')
},3000)
}
if (url) {
return resolve(url);
} else {
return reject('apiRequest failed!');
}
})
.catch(function(err){
return 'error on javascript code';
});
}
var p1 = apiRequest('urlOne',true);
var p2 = apiRequest(false,false);
var p3 = apiRequest('urlThree');
Promise.all([p1, p2, p3])
.then(function(res){
console.log('Promise.all', res);
}, error => {
console.log('Error on reject')
})
.catch(function(err){
console.error('err', err);
});
Result:
Promise.all [ 'urlOne', 'error on javascript code', 'urlThree' ]
var ar = url.asdasd.eadasd;
TypeError: Cannot read property 'eadasd' of undefined
If there is an error inside each promise, my code can catch it but if there is a timeout and the error happends after the promise finish I cant catch it and my code breaks, is there anyway to catch this error?
Is there any way to check error in each Promise in promise.all when there is a promise error?
By design, Promise.all() resolves when ALL promises you passed it have resolved or it rejects when any single promise in it rejects. It does not, by design, wait for all promises to resolve or reject and then give you all the results whether they resolved or rejected.
Functionality like that is typically named something like Promise.settle() and you can fairly easily build that type of functionality by just adding a .catch() handler to each promise you pass Promise.all() so that instead of rejecting, it resolves, but with a value that you can later tell that it actually rejected.
You can see several various implementations of .settle() type functionality in this answer:
ES6 Promise.all() error handle - Is .settle() needed?
If there is an error inside each promise, my code can catch it but if there is a timeout and the error happends after the promise finish I cant catch it and my code breaks, is there anyway to catch this error?
The way you have structured your setTimeout(), it is not connected at all to the promise that it is inside of. If you want them connected, then you have to wait to resolve until after the timeout is done and then, and only then, can you know if you should resolve or reject.
Since the code you show inside your setTimeout() looks like pseudo-code (that doesn't actually do anything yet), it's hard for us to see exactly what the point of the setTimeout() is to know exactly what you are trying to achieve and thus what a good suggestion would be.
This answer about using setTimeout() as part of a promise chain might be relevant:
using setTimeout on promise chain.
In that case, the timer is inserted into the promise chain so that things are sequenced before it and after it. As you show it now, it's a completely separate parallel path of execution that has no connection at all to your promise chain.
If all you're trying to do with the setTimeout() is to invoke a timeout if your api request does not return before the timer fires, then you can likely implement that by just calling reject() inside the setTimeout(). If the api request has already completed and already called resolve(), then calling reject() will do nothing at that point. If the api request has not yet finished, then calling reject() will reject the host promise.

How to have multiple success callbacks for 1 promise?

I have a function loadItems() that loads something async, and then fails or succeeds. Both fail and success callbacks must do something in the library, and then pass it on to the implementer, which can choose to implement fail, or success, or both, or none.
The problem I now have is if the library implements both the success and fail handler, it will always return a new promise that resolves to success.
This is what I have:
// The library:
function loadItems() {
return store.get('todo').then(function(rsp) {
// Save these locally
items = rsp.value || [];
}, function(rsp) {
// Do nothing, but let the user know
alert(rsp.error);
});
}
store.get('todo') (from yet another library) returns a promise. loadItems() has no control over it.
// The library implementer:
function init() {
loadItems().then(showItems);
}
What I want, and expected to happen:
loadItems() runs the async code from the library (store.get())
It implements success and fail because they have mandatory actions
It passes the success/failure on to the implementer
The implementer only implements success, because it doesn't care about errors, because the library handled it
So it uses .then(onSuccess) to 'add another success callback'
What happens instead (with an error):
The library's failure callback is executed
A new promise is passed on to the implementer
The new promise always resolves with success
The implementer's success callback is fired, with broken result, because the library's success callback didn't fire
Are Promises seriously too cool to have multiple, sync success handlers??
I can imagine even the source library (that defines store.get()) wants to handle the error (for logging sneakily), and then pass success/failure on.
My 'solutions':
Have loadItems()' failure callback throw an error. Unfortunately, that means init() has to catch it (or the browser whines about it). Not cool.
Use a simple callback to talk back to init(), instead of a promise, which only fires after a success. That's not good, because init() might choose to handle the error some day.
I'm sure I'm doing something wrong, but I don't see it.
If you want to have a reject handler to do something based on the error, but want the returned promise to still be rejected, you just rethrow the error (or return a rejected promise) after your reject handling code. This will allow the reject to propagate back through the returned promise.
// The library:
function loadItems() {
return store.get('todo').then(function(rsp) {
// Save these locally
items = rsp.value || [];
}, function(rsp) {
// Do nothing, but let the user know
alert(rsp.error);
// rethrow the error so the returned promise will still be rejected
throw(rsp);
});
}
Supplying a reject handler that does not throw or return a rejected promise tells the promise system that you have "handled" the error and the return value of your reject handler becomes the new fulfilled value of the promise. So, if you want the promise to "stay" rejected, but want to have a handler to do something based on the rejection (logging the rejection is very common), then you have to either rethrow the error in your reject handler or return a rejected promise.
While this may initially seem counter-intuitive, it gives you the most flexibility because you can either completely handle the error and let the returned promise be resolved "successfully" OR you can choose to tell the promise system that you want to propagate an error and you can even choose which error you want that to be (it does not have to be the same error).
The part of your question about multiple success handlers is a bit confusing to me. You can easily have multiple success handlers with any promise:
var p = someFuncThatReturnsSuccessfulPromise();
p.then(someSuccessHandler);
p.then(someOtherSuccessHandler);
If p is a successfully resolved promise, then these two success handlers will both be called in the order they are attached and what happens in someSuccessHandler will have no impact on whether someOtherSuccessHandler is called or not. If the original promise is resolved successfully, then both handlers will always be called.
If you chain your success handlers, then it is a completely different use case. This is completely different:
var p = someFuncThatReturnsSuccessfulPromise();
p.then(someSuccessHandler).then(someOtherSuccessHandler);
Because the second .then() handler is not attached to p, but is attached to p.then(someSuccessHandler) which is a different promise whose outcome is potentially influenced by what happens in someSuccessHandler.
The code in your question is chained. You are returning:
return store.get().then(...)
So, when the caller then chains onto that, the full chain is:
return store.get().then(yourhandler).then(theirhandler)
In this way, yourhandler can influence the outcome that is passed to theirhandler.
In addition to my first recommendation of just rethrowing the error, you could also have done this:
// The library:
function loadItems() {
var p = store.get('todo');
p.then(function(rsp) {
// Save these locally
items = rsp.value || [];
}, function(rsp) {
// Do nothing, but let the user know
alert(rsp.error);
});
return p;
}
Here you ware making sure that your handlers don't affect what is being returned. This can be done if you don't have any async operations in your handlers and you aren't trying to change the resolved value or rejected error of the original store.get() promise. I generally don't recommend this because it is a cause for problems if your handlers are doing other async things or want to influence the return values, but it can also be used in appropriate circumstances.

catch rejection in $q service without triggering success callbacks

I have a method that returns a $q (Q) promise:
var subtypesMetadataResolved = restService.getNodeSubtypesMetadata();
Now, when metadata is available, I want to run two functions to process them. First, I thought to chain them like this:
subtypesMetadataResolved.then(createNodes).then(prepareDataForWidgets)
But then I realized that since they both require data that is returned by the subtypesMetadataResolved promise I would need to return this data also from createNodes success callback so that it's passed into prepareDataForWidgets, which is not an option. So then I made it like this:
subtypesMetadataResolved.then(createNodes)
subtypesMetadataResolved.then(prepareDataForWidgets)
Which works like I need. But now the problem is how do I get my rejection callback called when subtypesMetadataResolved is rejected and don't get neither createNodes nor prepareDataForWidgets callbacks triggered in that case? I have the following, but after triggering nodeSubtypesMetadataErrorCb it also triggers createNodes callback:
subtypesMetadataResolved.catch(nodeSubtypesMetadataErrorCb);
subtypesMetadataResolved.then(createNodes)
subtypesMetadataResolved.then(prepareDataForWidgets)
Here is how I reject subtypesMetadataResolved:
EDIT:
function getNodeSubtypesMetadata(subtype) {
return $q.when("success!").then(function(){
debugger
throw new Error();
});
}
var subtypesMetadataResolved = getNodeSubtypesMetadata();
subtypesMetadataResolved.then(successCb1);
subtypesMetadataResolved.then(successCb2);
subtypesMetadataResolved.catch(nodeSubtypesMetadataErrorCb);
$q.all([
typesMetadataResolved,
subtypesMetadataResolved
]).then(init);
The problem is that you are assigning a promise with the handled error to subtypesMetadataResolved. If you call .then() on this, it will call the then() callbacks because the .catch() callback is essentially "handling" the error.
To solve this, assign the unhandled promise to your variable, and then call .catch()/.then() on that:
var subtypesMetadataResolved = getNodeSubtypesMetadata();
subtypesMetadataResolved.catch(nodeSubtypesMetadataErrorCb);
subtypesMetadataResolved.then(function(){
debugger
});
subtypesMetadataResolved.then(function () {
debugger
});
As a matter of style, I would suggest placing the catch line after the then lines, but this should have no observable effect on the code's behavior.
Looking at this line of code:
getNodeSubtypesMetadata().catch(nodeSubtypesMetadataErrorCb)
There are two promises. The first is the promise returned by getNodeSubtypesMetadata(), which is rejected. The second promise is returned by catch(nodeSubtypesMetadataErrorCb), which is fulfilled. So, when you assign the result of the above expression to a variable, you are getting the second, fulfilled, promise. Since you are interested in acting on the first promise, you need to change your code to this:
var subtypesMetadataResolved = getNodeSubtypesMetadata();
subtypesMetadataResolved.catch(nodeSubtypesMetadataErrorCb);
subtypesMetadataResolved.then(function(){
debugger
});
subtypesMetadataResolved.then(function () {
debugger
});
Edit: As an alternative, to have two functions that both act on the same fulfilled promise, you can just wrap those with a single function:
function nodeSubtypesMetadataFulfilledCb(metadata) {
createNodes(metadata);
prepareDataForWidgets(metadata);
}
subtypesMetadataResolved.then(
nodeSubtypesMetadataFulfilledCb,
nodeSubtypesMetadataErrorCb);

javascript promises onSuccess handler

Is there a handler method, the counterpart of fail, something like success to really get out of that async queue and continue with your normal function calls.
Let me elaborate more. let's say
getConnection()
.then(function(connection){
return self.getRecords() //some async routine, returns promise, does reject/resolve
})
.then(function(data){
return self.getDetail() //some async routine, returns promise, does reject/resolve
})
.then(function(data){ //I'm done here calling onResult, but this onResult may call several
self.onResult(data); //other functions down the road resulting in .fail() call
}) //I want to get out of this promise queue and continue with normal functions calls
.fail(function(info){
self.onFault(info); //any error onFault causes down the road shouldn't be q problem.
})
.done(function(){ //but this gets called all the time at end no matter success or fail
//release system resource like connection etc.
})
I've tried to explain the problem in comments, basically I'm done at self.getDetail() call, once it's success, I want to get out of promise queue, reason, if self.onResult(data) had a problem way afterwards, the .fail() is gonna get triggered too as it's sort of it dependency there.
I tried putting my call in the .done() method but done() gets called no matter what, success or fail.
I've a failed routine that gets called by .fail() function but don't know if there's a success handler.
Any lateral thinking welcome.
Edit - after Barmar's comments, can we do like this. (getConnection returns a promise and does reject/resolve
connections.getConnection(function(c){
return self.getMaster(c) //async routine, returns promise, does reject/resolve
}, function(info){
self.onFault(info) //any failures in getMaster, any error in onFault shouldn't be q business
})
.then(function(data){
return self.getDetail() //async routine, returns promise, does reject/resolve
}), function(info){
self.onFault(info)} //any failures in getDetail, any error in onFault shouldn't be q business
})
.fail(function(info){ //btw any errors, onFault causes down the road shouldn't be q problem- same for above onFault calls
self.onFault(info) //do I need this after above fail routines for each call?
})
.done(function(){ //ok, once everything's done, get out of promise queue
self.onResult(self.data) //any problem onResult causes down the road, should be it's own business
}) //release routine
//two independent functions out of async queue, chain of promises etc. any error in these functions should not affect the chain of promises or call it's fail handler. chain of promises should have been done up there.
onResult: function(data) {
console.log('do something with the data');
}
onFault: function(info) {
console.log('wonder what went wrong');
}
please suggests for the edit above
My Main Main requirement, anything happen after onResult, onFault shouldn't be q library business (fail), they are supposed to handle it on their own now (afterwards)
The first function you pass into a then is in itself a success handler.
The return value of an operation like:
doSomething().then(function() { ... }) is a new promise, which you can always store inside a variable, and then use multiple, independent then calls on:
var promise = someOperation().then(function(x) {
return doSomethingWith(x);
});
promise.then(function(processedX) {
// processedX is the return value of the function used to construct `promise`
// now for something completely different
}
promise.then(someOtherFunction);
You don't need to chain indefinitely, you can always "get out of the promise chain" by storing intermediate new promises into variables and using them somewhere else, possibly multiple times, and create several independent chains.
With that, you can attach a fail handler to one and the same promise in one chain, and have none attached to it in another. In your case, you'd want to store the entire chain up to the handler calling self.onResult in a variable, use the fail handler on that variable, and continue with the rest of the code using the same variable.

Categories