18

I am working in a nodejs project and want to skip promise in a chain. Below is my code. In the first promise block, it will resolve a value {success: true}. On the second block I want to check the value of success, if true I want to return the value to the called and skip the rest of the promises in this chain; while continue the chain if the value is false. I know I can throw an error or reject it on the second block but I have to handle error case which it is not an error case. So how can I achieve this in promise chain? I need a solution without bring any other 3rd party library.

new Promise((resolve, reject)=>{
    resolve({success:true});
}).then((value)=>{
    console.log('second block:', value);
    if(value.success){
        //skip the rest of promise in this chain and return the value to caller
        return value;
    }else{
        //do something else and continue next promise
    }
}).then((value)=>{
    console.log('3rd block:', value);
});
Joey Yi Zhao
  • 37,514
  • 71
  • 268
  • 523
  • you can't ... just like you can't take a link out of the middle of a chain and think you have one chain ... you have two! Of course "specifically crafted" values can be sent and detected along the chain – Jaromanda X Feb 02 '17 at 04:10
  • You can't give back the value to caller. Result of invoking promise returns promise object, not the value that you've returned. – modernator Feb 02 '17 at 05:12
  • Possible duplicate of [How to handle the if-else in promise then?](http://stackoverflow.com/questions/33257412/how-to-handle-the-if-else-in-promise-then) – Daniel B Feb 02 '17 at 10:41

3 Answers3

14

Simply nest the part of the chain you want to skip (the remainder in your case):

new Promise(resolve => resolve({success:true}))
.then(value => {
    console.log('second block:', value);
    if (value.success) {
        //skip the rest of this chain and return the value to caller
        return value;
    }
    //do something else and continue
    return somethingElse().then(value => {
        console.log('3rd block:', value);
        return value;
    });
}).then(value => {
    //The caller's chain would continue here whether 3rd block is skipped or not
    console.log('final block:', value);
    return value;
});
jib
  • 40,579
  • 17
  • 100
  • 158
  • 1
    The rest of my promise in the chain is very long. If I do that, I have to create nested promise chain which I don't like. Is there a better way to do that? – Joey Yi Zhao Feb 02 '17 at 04:18
  • 1
    @ZhaoYi There's nothing wrong with nesting here. You nest statements inside an `if` don't you? It's a conditional branch of the promise chain, which is idiomatically correct IMHO. See [my answer to a similar question](http://stackoverflow.com/a/40413396/918910). – jib Feb 02 '17 at 04:20
  • 1
    @ZhaoYi - Any conditionally executed `.then()` handler has to be nested because you are creating a short sub-branch of the main chain. The rest of your chain can continue at the top level just fine (which this answer doesn't show). Branching requires nesting. – jfriend00 Feb 02 '17 at 04:22
  • @jfriend00 Top level added. Good idea. I also pass through `value` or code won't make sense. – jib Feb 02 '17 at 04:25
  • I can relate to the OP, as the "then nesting" is considered an anti-pattern. So i rather let the structure flat and adding return statements at the beginning of each then to handle the "skips" – BennyHilarious Jan 15 '23 at 13:37
  • 1
    @BennyHilarious glad that works for you, though only *unnecessary* nesting is an anti-pattern. Using nesting for conditionals is an [accepted pattern](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#nesting) IMHO. Though in 2023 I think the best solution here is to use [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await). – jib Jan 18 '23 at 17:07
3

If you don't like the idea of nesting, you can factor the remainder of your chain into a separate function:

// give this a more meaningful name
function theRestOfThePromiseChain(inputValue) {
    //do something else and continue next promise
    console.log('3rd block:', value);

    return nextStepIntheProcess()
        .then(() => { ... });
}

function originalFunctionThatContainsThePromise() {
    return Promise.resolve({success:true})
        .then((value)=>{
            console.log('second block:', value);
            if(value.success){
                //skip the rest of promise in this chain and return the value to caller
                return value;
            }

            return theRestOfThePromiseChain(value);
        });
}

Aside from that, there isn't really a way to stop a promise mid-stream.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • 3
    A separate function is still technically nesting. – jib Feb 02 '17 at 04:33
  • 2
    @jib Maybe from the perspective of the execution it is, but as far as the way the code is formatted (which I think is what OP is concerned about), it avoids excessive nesting and long stretches of code where it's hard to see where they begin and end. – JLRishe Feb 02 '17 at 05:07
0

You could also continue to throw the error down the chain to a final catch block if you like.

const test = () => {
  throw new Error('boo');
}

const errorHandler = (e) => { throw e };

Promise.resolve()
        .then(console.log('then1'))
        .then(() => test())
        .then(f => console.log('then2'), errorHandler)
        .then(f => console.log('then3'), errorHandler)
        .catch((err) => console.log('caught', err));

// whoo
// then1
// caught Error: boo
4m1r
  • 12,234
  • 9
  • 46
  • 58
  • i thought about it as well, but this seems more abusive and anti-pattern than actual promise nesting (seen as anti-pattern) – BennyHilarious Jan 15 '23 at 13:39