I am having chained promises handled by one catch block.
If one of the promises rejects, I want to do the clean up in the catch block depending on which step failed.
However, the cleanup itself also returns a Promise which needs to fulfill before I can finally reject the function.
The issue is: What if my cleanup promise does not resolve? Wouldn‘t this cause a loop of .then & .catch? How can I make sure that my cleanup executes before ending the function?
Example:
return promise1()
.then(() => promise2)
.then(() => promise3)
.then(() => {
return { result: xyz }
}
.catch((error) => {
return cleanup()
.then(() => {
throw(error)
}
}
Personally, I would just use async / await syntax. This allows for simpler control of waits and resolve / reject values.
const myAsyncFun = async () => {
try {
await promise1()
await promise2
await promise3
return { result: "xyz" }
}
catch (error) {
try {
await cleanup()
}
catch (cleanupError) {
// handle cleanup error
}
finally {
throw error // reject with the original error
}
}
}
Original answer below...
Sounds like you want to use Promise.prototype.finally()
When the promise is settled, i.e either fulfilled or rejected, the specified callback function is executed
.catch(error =>
cleanup()
.catch(cleanupErr => {
// handle cleanup error here
})
.finally(() => Promise.reject(error))
)
This will ensure that the result is a rejected promise (rejecting with error) whether cleanup() resolves or not.
Demo
const cleanup = (fail) =>
!fail ? Promise.resolve("cleanup good") : Promise.reject("cleanup bad")
const go = (failMain, failCleanup) => {
const p = !failMain
? Promise.resolve("main good")
: Promise.reject("main bad")
return p.then(() => ({ result: "xyz" }))
.catch(error =>
cleanup(failCleanup)
.catch(cleanupErr => {
// handle cleanup error here
console.warn(cleanupErr)
})
.finally(() => Promise.reject(error))
)
}
(async () => {
console.info("No errors")
await go(false, false).then(console.log, console.error)
console.info("Main error only")
await go(true, false).then(console.log, console.error)
console.info("Main and cleanup errors")
await go(true, true).then(console.log, console.error)
})()
To your first question: No! Once your program is in the catch block, it has nothing to do with the promises in it, so it will not catch cleanup().
To your second question: You can of course write another catch block for it and throw an error with a relevant error message (For instance "Cleanup could not be executed")
You're good to go.
Your current code will await the cleanup function properly, because returning a promise from a chaining callback (then, catch, finally) makes both resolution and rejection propagate correctly.
Even if the cleanup function failed, it wouldn't cause any problems; the rejection would propagate to the outermost (returned) promise, and the caller code could catch it the same way as a failure from the promise1, promise2 or promise3 functions.