If you have a Promise that you're executing, you can chain multiple things quite nicely by simply returning the value as it "passes through".
For example:
new Promise((resolve) => {
resolve('some value')
})
.then((value) => {
console.log('1', value)
return value
})
.then((value) => {
console.log('2', value)
return value
})
This will console log
1 some value
2 some value
And you can add more .then()
to it. As many as you like. Just remember to "play ball" by passing the value. In fact, you can actually pass a different value. Like this for example:
new Promise((resolve) => {
resolve('some value')
})
.then((value) => {
console.log('1', value)
return value
})
.then((value) => {
console.log('2', value)
return value.toUpperCase()
})
.then((value) => {
console.log('3', value)
return value
})
Demo here.
This'll console log
1 some value
2 some value
3 SOME VALUE
But how do you do the same with multiple .catch()
?
This is NOT how you do it:
new Promise((resolve, reject) => {
reject('some reason')
})
.catch((reason) => {
console.warn('1', reason)
return reason
})
.catch((reason) => {
console.warn('2', reason)
return reason
})
Demo here.
When you run that you just get:
1 some reason
To chain catches you have to re-raise (aka re-throw) it:
new Promise((resolve, reject) => {
reject('some reason')
})
.catch((reason) => {
console.warn('1', reason)
throw reason
})
.catch((reason) => {
console.warn('2', reason)
})
Demo here.
The output if you run this is:
1 some value
2 some value
But you have to be a bit more careful here. Note that in the second .catch()
it doesn't re-throw the reason
one last time. If you do that, you get a general JavaScript error on that page. I.e. an unhandled error that makes it all the way out to the web console. Meaning, you have to be aware of errors and take care of them.
Why does this matter?
It matters because you might want to have a, for example, low level and a high level dealing with errors. For example, you might want to log all exceptions AND still pass them along so that higher level code can be aware of it. For example, suppose you have a function that fetches data using the fetch API. You use it from multiple places and you don't want to have to log it everywhere. Instead, that wrapping function can be responsible for logging it but you still have to deal with it.
For example, this is contrived by not totally unrealistic code:
let fetcher = (url) => {
return fetch(url)
}
fetcher('http://example.com/crap')
.then((response) => {
document.querySelector('#result').textContent = response
})
.catch((exception) => {
console.error('oh noes!', exception)
document.querySelector('#result-error').style['display'] = 'block'
})
fetcher('http://example.com/other')
.then((response) => {
document.querySelector('#other').textContent = response
})
.catch((exception) => {
console.error('oh noes!', exception)
document.querySelector('#other-error').style['display'] = 'block'
})
Demo here
Notice how each .catch()
handler does the same kind of logging but deals with the error in a human way differently.
Wouldn't it be nice if you could have a general and central .catch()
for logging but continue dealing with the errors in a human way?
Here's one such example:
let fetcher = (url) => {
return fetch(url)
.catch((exception) => {
console.error('oh noes! on:', url, 'exception:', exception)
throw exception
})
}
fetcher('http://example.com/crap')
.then((response) => {
document.querySelector('#result').textContent = response
})
.catch(() => {
document.querySelector('#result-error').style['display'] = 'block'
})
fetcher('http://example.com/other')
.then((response) => {
document.querySelector('#other').textContent = response
})
.catch(() => {
document.querySelector('#other-error').style['display'] = 'block'
})
Demo here
Here you get the best of both worlds. You have a central place where all exceptions are logged in a nice way, and the higher level code only has to deal with the human way of explaining that something went wrong.
It's pretty basic but it's probably useful to somebody else who gets confused about how to deal with exceptions in promises.