Reputation: 503
I have a function that returns a Promise. When I am done using that Promise in the .then()
or .catch()
blocks, I always want to execute the same cleanup code. My current setup is this:
const promiseWithFinally = () => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
}).finally(() => console.log('finally done'))
}
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'))
What I want to happen is that either then done
or catch done
get logged first and then finally done
. However it seems to get executed in the exact opposite order - when I resolve the Promise after a timeout of 5 seconds finally done
gets logged first after 5 seconds and then then done
immediately afterwards.
What am I doing wrong or is it possible to do this in general? I know I could just append the .finally()
to each individual function call, but since it's always the same I'd like to put it in the function definition.
Upvotes: 5
Views: 978
Reputation: 13
There is a solution that involves promises. The caveat is that it will not work if users of the returned promise object perform a similar "hack".
const promise = new Promise((resolve, reject) => resolve());
promise.finally(() => promise.finally(() => console.log("cleanup")));
promise.then(() => console.log("do me first!"));
The concept is that the first callback executed in the promise chain can append whatever it wants to the end of the promise chain. Of course, if the next callback does the same thing you're out of luck, which is the limitation of this approach.
Upvotes: 0
Reputation: 151
Assuming you know the rest of the handlers to that promise are going to be attached synchronously, and all actions in the handler are synchronous, it is possible, albeit a little bit hacky.
Simply have the finally handler re-attach itself at the end:
const promiseWithFinally = () => {
const thisPromise = new Promise((resolve, reject) => {
// Rejection example
setTimeout(reject, 200);
}).finally(() => {
setTimeout(() => {
thisPromise.finally(() => console.log('finally done')).catch(() => {});
}, 0);
});
return thisPromise;
};
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'));
Upvotes: 0
Reputation: 569
No it is not possible as you cannot rely on when finally is being ran.
const cleanupFunc = () => {
console.log('Cleaning up.');
};
const someAsyncMethod = (thenFunc, catchFunc) => {
new Promise(
(resolve, reject) => {
setTimeout(() => resolve(), 5000);
},
)
.then((...args) => {
try {
thenFunc(...args);
} catch (err) {
}
cleanupFunc();
})
.catch((...args) => {
try {
catchFunc(...args);
} catch (err) {
}
cleanupFunc();
});
};
someAsyncMethod((result) => console.log('Then done'), (err) => console.log('Catch done'));
Al though it is not possible to rely on finally, what you can do is write a function that needs to do some asynchronous operation returning a promise. In my example this operation is waiting on a 5 second timeout but his can also, for example, be an asynchronous api call that returns a promise.
The next step would be to add a then and a catch call to the promise the asynchronous operation returns that both begin with a try clause in which you call the callback parameter that belongs to the type of resolve (thenFunc for then, catchFunc for catch) followed by a catch which doesn't do anything and ends by calling the cleanup function. in this way you are certain that whatever happens during running the then or catch callback, the cleanup function is being called.
Upvotes: 1
Reputation: 11600
No it's not possible. Finally is for cleaning up after a given promise, not for its then
or catch
methods.
What you can do is pass then
and catch
methods to the function which will be appended before finally
:
const promiseWithFinally = (chain) => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
setTimeout(resolve, 1000);
}).then(chain.then, chain.catch).finally(() => console.log('finally done'))
}
promiseWithFinally({
then: () => console.log('then done'),
catch: () => console.log('catch done')
})
Upvotes: 1