///
// Sandwiches a call to an asynchronous function between two other
// (synchronous) functions. Intended for tracking the number of calls
// inflight to a freedom provider, which may fail to resolve if the
// provider's communication channel is destroyed while the call is
// pending (this can happen even on a normal call to the provider's
// close method).
// Throws synchronously if the before function raises an error; the
// returned promise rejects if the after function raises an error.
export function wrap(
wrapper:Wrapper,
f:() => Promise) : Promise {
try {
wrapper.before();
} catch (e) {
wrapper.after();
throw e;
}
return f().then((result:T) => {
wrapper.after();
return result;
}, (e:Error) => {
wrapper.after();
throw e;
});
}
export class Wrapper {
private counter_ = 0;
private fulfillDestroyed_ :() => void;
private rejectDestroyed_ :(e:Error) => void;
private onceDestroyed_ = new Promise((F, R) => {
this.fulfillDestroyed_ = F;
this.rejectDestroyed_ = R;
});
constructor(private destructor_ :() => void) {}
public discard = () : void => {
this.after();
}
public onceDestroyed = () : Promise => {
return this.onceDestroyed_;
}
public before = () : void => {
this.counter_++;
}
public after = () : void => {
this.counter_--;
if (this.counter_ < 0) {
try {
this.destructor_();
this.fulfillDestroyed_();
} catch (e) {
this.rejectDestroyed_(e);
}
}
}
}