Search code examples
javascriptasp.nettypescriptpromisepagemethods

Abort a page method but also use a Promise to get the return value


So, I wrap page methods in a Promise<T> like so:

return new Promise<T>((done, fail) =>
   window.PageMethods.SomeMethod(arg1, arg2, done, fail)
);

(little more complicated than that, but that's basically it)

But, what I want to do is be able to also abort the page method. This is done by calling window.PageMethods._staticInstance.SomeMethod instead, which returns a request object which can be used to abort the page method. Something like:

const request = window.PageMethods._staticInstance.SomeMethod(arg1, arg2, done, fail);

...

const executor = request.get_executor();
if (executor.get_started())
   executor.abort();

Ok, but how can I combine these two processes? It feels impossible so far, which I guess it probably is...

return new Promise<T>((done, fail) => {
   const request = window.PageMethods._staticInstance.SomeMethod(arg1, arg2, done, fail);
   // do what with request???
});

I can't get the request out of the promise, but I also can't not call the page method from within the promise. I feel like there might be a small chance that I could get around this problem using either a closure trick or else using two promises, but I haven't been able to figure it out so far.


Solution

  • Ok I think I solved it by using a 'closure trick'.

    The functions that I pass to the page method cannot be defined later, but they can call functions that are defined later. Additionally, since the promise executor is executed immediately, this works.

    Create two handler placeholders that don't exist yet, and call them before they exist from the handlers that we'll give to the page method.

    let deferredResolve: (value: T) => void;
    const lazySuccess = value => deferredResolve(value);
    
    let deferredReject: (result?: any) => void;
    const lazyFail = result => deferredReject(result);
    

    Call the page method outside of the promise, passing the incomplete handlers. The page method will not use them yet, so it's ok that they're incomplete.

    const request =
       window
       .PageMethods
       ._staticInstance
       .SomeMethod(arg1, arg2, lazySuccess, lazyFail);
    

    Now create a promise, and in the promise's executor function, we now define our handlers with calls to the promise resolve and reject functions. (In fact I realized that they can actually just be assigned the resolve and reject functions themselves directly.)

    const promise = new Promise<T>((resolve, reject) => {
       deferredResolve = resolve;
       deferredReject = reject;
    });
    

    Now we have both a promise and a request and the request is freed from the promise. This works because the promise calls its executor function right away, but the page method does not call its success and fail handlers until later.

    I suspect this trick could be very useful in other situations.