How to handle rejections in Bluebird without breaking a Promise chain? - javascript

Let's say I'm building a typical RSS-reader. I'm parsing several feeds and write all their episodes to DB:
const processEpisode = (episode)=>
fetchMetadata(episode)
.then(parseMetadata)
.then(writeToDb)
const processFeed = (feed)=>
fetchEpisodes(feed) // returns [Episode]
.map(processEpisode, { concurrency: 3 })
// main
getFeeds() // returns [Feed]
.map(processFeed, { concurrency: 3 })
.catch(err=> console.log(err))
We get all the feeds
For each feed emit processFeed()
For each episode emit processEpisode() from processFeed()
However, if fetchMetadata(episode) for some episode for some feed throws a rejection, all the chain is broken and immediately falls into global .catch(err=> console.log(err)).
In normal situation we need to do something with unprocessed episode, but the least should be processed normally. One solution is to wrap processEpisode() in an outer Promise and handle in-place.
const processEpisode = (episode)=>
new Promise((resolve, reject)=> {
fetchMetadata(episode)
.then(parseMetadata)
.then(writeToDb)
.then((result)=> resolve(result))
.catch((err)=> {
// something bad happened
// process and error, but resolve a fullfilled Promise!
resolve(true)
})
})
However, I suppose it's an obvious anti-pattern. And if after processEpisode() there is another element in higher level Promise chain, it will fail 'cause processEpisode will resolve true instead of real result.
Is there an elegant way to solve such problems? I've looking through finally statement in Bluebird, but I'm not sure it's the best way.
Thank you!

Just put a .catch() handler directly on processFeed() so you can process the rejection locally and turn it into a resolved promise which will allow everything else to continue:
// main
getFeeds() // returns [Feed]
.map(function(item, index, length) {
return processFeed(item, index, length).catch(function(reason) {
// do whatever you want to here, this will "handle" the rejection
// and turn it into a resolved promise
// whatever you return here will become the resolved value
});
}, { concurrency: 3 })
.catch(err=> console.log(err))
Note: You don't need the extra wrapping promise. Adding a .catch() handler and returning a normal value from the .catch() handler will turn the rejected promise into a resolved promise as the rejection is considered "handled" at this point. Whatever value you return from the .catch() handler becomes the resolved value of the parent promise.
A .catch() handler will only keep the promise rejected if it returns a rejected promise or throws.

Related

What are the consequences of handling a rejected promise too early?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Handling a rejected promise too early has consequences further down the promise chain.
What kind of consequences can be there? In which cases should early handling of rejections be avoided and in which cases should early handling of rejections be preferred?
The critical part to understand is that catch() also returns a promise.
Consider the following example. Since the intermediate catch has no return BUT catch() itself returns a promise, the final catch never fires and the final then() does ... receiving a result of undefined from the prior catch() promise
const func = (val) => {
return new Promise((resolve,reject)=> {
// not really asynchronous, only contrived for demo
val === 1 ? resolve(val) : reject('Oooops')
}).catch(err=> {console.log('Err in func catch:', err)})
}
func(200).then(res=> console.log('Then res=', res))// receives undefined from prior catch
.catch(err=> console.log('Never gets called'))
So you could do several things differently here depending on preferred logic.
You could return a different expected value from the intermediate catch and look for that in the final then, or return another rejected promise or thrown error to be caught in the final catch().
Or...don't use the middle catch at all if it serves no value intercepting it.
Example use case for using the intermediate catch would be:
A map() of request urls passed through a function that returns the request promise back to your map array but you are OK if not all succeed.
Without the intermediate catch intercepting, the whole chain would break at first bad request. Instead you can return something from intermediate catch that you filter out in a Promise.all().then()

Unhandled Rejection Error - why? // Promise Chain Design decision?

A) See please the code below.
If I use line // 1 I get the onError method called, that's OK.
But if I use line // 2 (and comment out // 1), then onError is still called but there is also an unhandled rejection error. Why? This kind of forces me to do chaining as in // 1. If I don't do chaining but call catch on the original promise then I get the unhandled rejection error.
B) Why the calls p.then and p.catch do not return the original promise p? I find this weird. In all libs which I have seen to allow chaining, the original object (i.e. the this object) is returned. In JavaScript when we do promise1.then(...) a new promise2 is a returned which is not the promise1. I find this weird. What is the logical reasoning behind this? To me it seems like an unnecessary complication and even a bad practice. Yet another gotcha which one needs to remember when working with JavaScript. But OK... I am sure smart people decided to take this approach so... what is the reasoning behind this decision? Any ideas?
function onSuccess () {
console.log('Success occurred!')
}
function onError () {
console.log('Error occurred!')
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject()
}, 2000)
})
var p1 = promise.then(onSuccess);
var p2 = p1.catch(onError); // 1 //
// var p2 = promise.catch(onError); // 2 //
console.log("DONE!");
To answer your first question, this is quite simple:
You have 2 promise object when you get to those lines you mentioned: one is held by promise variable, and the other by p1 variable. When you chain the catch directly to promise, it does not apply to the promise that is coming back from the then chaining - which is stored in p1- so you get unhandled promise rejection error on the promise that comes back from the then clause.
But, when you chain it to p1, which is already chained to promise, then the catch clause covers them both so no error here.
The difference essentailly is that line //1 can catch rejections / errors both from the original promise and then .then promise, but line //2 catches only rejects from original promise, leaving rejectes from the .then unhandled.
About 2nd question I'm sorry, but too high level for me and I don't wanna give incomplete / inaccurate answer.

What will happen when return new Promise((resolve, reject) => {}) forgot to call either resolve or reject? [duplicate]

This question already has answers here:
Are JavaScript forever-pending promises bad?
(2 answers)
Closed 3 years ago.
The problem is like this
function demo() {
return new Promise((resolve, reject) => {
...
// The problem here!!
//I just found in some rare case we failed to call resolve or reject
})
}
demo()
.then(res => {
console.log('resolve')
console.log(res)
})
.catch(rej => {
console.log('reject')
console.log(rej)
})
.finally(() => {
console.log('why')
})
When I failed to call resolve or reject, even the finally block is not called! Why ?
I had thought it was a bug then I found the original author seemed to do that on purpose that if he did not call either resolve or reject, none of then/catch/finally should be called, i.e. in that case no follow-up action should be taken.
But is this a valid way to handle the situation that no follow-up action should be taken ? Will it cause any trouble ?
----- update -----
Even though my question was marked duplicated I am still not satisfied with the answers I got. Originally I had thought it was a bad idea to let promise stay in pending state forever.
But the answer in that SO said "There should be no side effect."
Does never resolved promise cause memory leak? also said "In short - at least in modern browsers - you don't have to worry about unresolved promises as long as you don't have external references to them". So it seems ok to let promise stay in pending if that is the purpose.
Internally, a promise can be in one of three states:
Pending, when the final value is not available yet. This is the only state that may transition to one of the other two states.
Fulfilled, when and if the final value becomes available. A fulfillment value becomes permanently associated with the promise. This may be any value, including undefined.
Rejected, if an error prevented the final value from being determined. A rejection reason becomes permanently associated with the promise. This may be any value, including undefined, though it is generally an Error object, like in exception handling.
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
In your case, the promise is in the pending state and calling demo function will always in wait for the promise status to be fulfilled or rejected.
A promise is always expected to either resolve or reject.
If you intend to do a no follow up, you may resolve with an empty dataset or reject with an error code that suits your use case.
You may use Promise.race to check if a promise was finished on time.
So if you forgot to call resolve or reject into your promise, then Promise.race still be resolved or rejected after delay.
var promise1 = new Promise(function(resolve, reject) {
setTimeout(reject, 500);
});
var promise2 = new Promise(function(resolve, reject) {
});
Promise.race([promise1, promise2]).then(function(value) {
console.log(value);
}).catch(err => console.log('promise rejected'));

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.

What is the difference between returning and not returning a promise inside the then-block of another promise?

What exactly is the difference? In the two scenarios below, what type of difference does myPromise have?
Here is the example:
Case 1. Returning a promise
function test() {
return somePromise.then(() => {
return anotherPromise.then(res => {
console.log('Result:', res);
});
}).catch(err => {
console.error(err);
});
}
const myPromise = test();
Case 2. Not returning a promise
function test() {
return somePromise.then(() => {
anotherPromise.then(res => {
console.log('Result:', res);
});
}).catch(err => {
console.error(err);
});
}
const myPromise = test();
The first case is "promise chaining" and it a very common and useful tool. By returning a promise from a .then() handler, you are telling the parent promise that you want it to wait until the returned promise is done before resolving itself. This allows you to create a sequence of events and know when the entire sequence is done by monitoring the top level promise. Anyone watching the parent promise will only see a resolve or reject when the "chained" promise is also resolved or rejected. Errors from the chained promise will propagate upwards.
Also, the resolved value of the parent promise will become the resolved value of the chained promise.
The second case is just starting a new promise chain that is not connected to the original one in any way. It won't propagate errors and the parent promise won't wait for it. It's kind of like a "fire and forget" operation where you're running it separately, but not connecting it at all to the parent operation.
This is usually a "bug" (though there are occasionally situations where you want to start a new async operation and are not reporting its success or failure back to anyone). In the code you show, if anotherPromise rejected, you'd get a warning about an unhandled rejection because it is not chained and will not propagate up to your .catch().
Well for one, the second one's catch block would not be hit if there was a problem with the nested promise.
First case will wait until the anotherPromise will be resolved or rejected, then pass the execution to the next then of somePromise with the resolved or rejected value of the anotherPromise. So the chain is made by the values of both anotherPromise and somePromise.
Second will just run the inner promise, forget about it and return the execution to the next then of somePromise with the resolved or rejected value of the somePromise. So the chain is made by the values of only somePromise.
In the first case, myPromise will not resolve until both somePromise and anotherPromise have resolved.
In the second case, myPromise will resolve as soon as somePromise resolves. Additionally, the catch handler will not catch any error from anotherPromise if it rejects.
The latter is generally an antipractice because it is an example of "fire and forget" code.
Bear in mind that you are also building up a "pyramid of doom" there. A preferable approach to both of those examples would be:
function test() {
return somePromise
.then(() => anotherPromise)
.then(res => {
console.log('Result:', res);
})
.catch(err => {
console.error(err);
});
}
const myPromise = test();

Categories