2

I am struggling with how to handle this special case. I know I could solve this with chained callbacks, but it seems like almost a poster child for promises:

I have a parent method that needs to execute three async things in order (specifically getting confirmation from a user). We'll call them func1 func2 and func3. Now I can have each of these return a promise and chain those and that is all working great. The issue I run into is:

func1 needs to set a state, wait on the rest of the chain to run, and then unset that state.

demonstration pseudocode:

function wrapper(){
    func1()
        .then(func2())
        .then(func3());
}
function func1(){
    return new Promise(function(resolve, reject){
        //do some async stuff that itself returns a promise :)
        async1().then(function(value){
            //set some global states based on the value for the duration of this chain
            resolve(); //Note NOT returning value as it's irrelevant to the other functions
            //!!! clean up the global states regardless of future failure or success, but after one of those.
        }, reject); //if async1 rejects, just pass that right up.
    });
}
function func2(){
    //do some logic here to decide which asyn function to call
    if (/*evaluates to */true){
        return async2(); //another async function that returns a promise, yay!
    } else {
        return async2_1(); //also returns a promise.
    }
}
function func3(){
    //do some dom updates to let the user know what's going on
    return Promise.resolve(async3()).then(function(){
        //update the dom to let them know that everything went well.
    });//returns this promise chain as a promise which the other chain will use.
}

The part I'm struggling with is the line in func1 after resolve(); Note that as said there async1 which I'm calling in func1 does return a promise so I am already working with promises a lot here. I need the cleanup to happen AFTER the promise returned from func3 resolves.

Ideally this would all be contained in func1 in some way, but I'm also ok with a semi-global variable (this entire block will be wrapped in a larger function).

lassombra
  • 417
  • 3
  • 15
  • You'll want to have a look at the [disposer pattern](http://stackoverflow.com/q/28915677/1048572) – Bergi Aug 29 '15 at 21:35
  • @Bergi while I understand the concept of the disposer pattern, it requires that it wraps all of your other promises. In this case I'd have to wrap the wrapper function in a disposer pattern. That is definitely NOT what I want as I want to keep this close. Basically in this case I need a way to get a promise out of the resolve and have no idea how to do that. – lassombra Aug 29 '15 at 21:40
  • You don't have to wrap the wrapper function. It would simply be something like `function wrapper() { func1(value => func2().then(func3)) }`. No, `resolve` will never return a promise. You'll have to accept a callback to `func1`. – Bergi Aug 29 '15 at 21:44
  • There is no way to determine for the promise code what you consider to be "the rest of the run" that should be awaited. You need to be explicit. There's no way around it (except some magic which you don't want to consider here). – Bergi Aug 29 '15 at 21:45
  • @Bergi unfortunately that is really annoying and limiting. I could pass a callback out and "capture" it before continuing and then call that callback at the very end of the chain... – lassombra Aug 29 '15 at 21:48
  • Sure, you could do that as well, especially if consider using global state anyway. This is just a lot more ugly, annoying and error-prone than the simple disposer pattern. – Bergi Aug 29 '15 at 21:55
  • @Bergi how would you suggest using the disposer pattern in this example? func1 which is what needs to use it has no knowledge of the rest of what's going on (note that my pseudocode is simplified, but there is more complexity involved, specifically that func2 and func3 are both variable depending on a few pieces of user state. – lassombra Aug 29 '15 at 22:01
  • Just like in the example I posted above, `func1` would take a callback for "the rest that is going on". – Bergi Aug 29 '15 at 22:02
  • @Bergi Just noticed your second comment, somehow missed that. – lassombra Aug 29 '15 at 22:15
  • @Bergi please provide an actual answer so I can accept it. You were right, the disposer pattern was EXACTLY what I needed. Thank you for introducing me to it and being patient with me as I wrapped my head around it. – lassombra Aug 29 '15 at 22:49

1 Answers1

3

You'll want to use the disposer pattern (and avoid the Promise constructor antipattern):

function with1(callback) {
    return async1() // do some async stuff that itself returns a promise :)
    .then(function(value) {
        // set some global states based on the value for the duration of this chain
        const result = Promise.resolve(value) // or whatever
        .then(callback);

        return result.then(cleanup, cleanup);
        function cleanup() {
            // clean up the global states regardless of the failure or success of result
            return result;
        }
    });
}

You can use this like

function wrapper() {
    with1(() =>
        func2().then(func3) // whatever should run in the altered context
    );
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375