I'm trying to write a function that performs an asynchronous task and returns a promise while ensuring cleanup occurs after any callbacks are fulfilled. However to do this, it seems I need to know the callback in advance so I can ensure that it happens before the cleanup happens.
Currently, the general structure of the function looks like this:
function doSomethingWithResourceAsync(someParameter, usePreparedResourceCb) {
var resource = acquireResource(someParameter);
return prepareResourceAsync(resource)
.then(usePreparedResourceCb)
.finally(doCleanup);
function doCleanup() {
releaseResource(resource);
}
}
To call it, I would do this:
doSomethingWithResourceAsync(myParameter, myCallback)
.then(andSoOn);
function myCallback(proxyObj) {
return doMagicAsync(proxyObj);
}
This is the only way I can get it to work.
However, I want to write it in a way that I can chain my callback instead while not having to pass around a cleanup callback. So I'd like to call it like this:
function doSomethingWithResourceHopefullyAsync(myParameter) {
var resource = acquireResource(someParameter);
return prepareResourceAsync(resource)
.finally(doCleanup); // uh oh
function doCleanup() {
releaseResource(resource);
}
}
doSomethingWithResourceHopefullyAsync(myParameter)
.then(myCallback) // too bad, already cleaned up
.then(andSoOn);
This doesn't work however because the cleanup happens before myCallback gets control and messes things up.
If possible, how can I structure my method to accomplish my goal? Or is what I have the best I can do for this situation?
I have a feeling I could use deferreds to accomplish my goal but I don't know how to set that up to make this work.
The API I'm trying to develop is to be consumed by users who won't necessarily know the intricacies of asynchronous methods so I want to hide that as much as possible.
What you have is the disposer pattern. Props for figuring it out on your own :)
"Passing" the callback in is necessary because it creates a scope which is what effectively enables the cleanup. You'd know how the callback is "done" by it returning a promise. The fact you need a scope is fundamental, because it's what well... scopes the cleanup. Binding resource allocation to instantiation via scope (RAII) is a useful technique for what you're doing.
I would do something like:
function withResource(handler){
return acquireResource(). // important to return to chain, like your code
then(handler).finally(cleanup);
}
Which is effectively what you already have.
As the comments suggest, bluebird's using is a very useful abstraction, it returns disposers which give you a lot of power for cleanup and clean up a lot of type errors. I highly recommend it (though I'm obviously biased).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With