Search code examples
javascriptjquerypromisebluebirdchain

How to break a promise chain?


I'm aware that there are similar questions, but I haven't seen any that address this chaining pattern.

I have the following:

    var runTests = function (chain, resolutionTest) {
        return chain.then(function (result) {
            if (result)
                return result; // Early return if the previous tests were successful.  This is where I want to prevent other attempts.
            const attempt = tryOpenStream(resolutionTest).then(streamToDom);
            return attempt;
        });
    }

    // from someplace else
    numTests = resolutionTests.length;
    return resolutionTests.reduce(runTests, Promise.resolve()); // start reduce with an empty promise 

The problem that I'm encountering is that I'm calling tryOpenStream many times even after I've captured a result.

Options I'm considering:

  • Raise some global flag that just prevents further execution from within the chain. Yuck, because the chain still proceeds, it's just emptied.
  • throw new Error(result) instead of return result. This would break the chain (I think...) but it's misusing Error and would be easily misunderstood by another developer.

How can I break this chain at return result;?

UPDATE 1

I'm trying the following:

   var makeTest = function (runMoreTests, resolutionTest) {
        return function runTest() {
            return tryOpenStream(resolutionTest).then(streamToDom).then(function (result) {
                if (result)
                    return result;
                else
                    return runMoreTests();
            });
        };
    }

    return resolutionTestBuilder.buildTests().then(function (resolutionTests) {
        numTests = resolutionTests.length;
        return resolutionTests.reduceRight(makeTest, function () { Promise.reject("No resolutions succeeded.") })();
    });

However no calls to runTest are invoked. This is a bit of new syntax for me so I'll research some and update with any findings.

UPDATE 2

I was missing the () to invoke the reduceRight. Though now I'm seeing that reject called even with success... though when I step through, that rejection isn't invoked. It's as if by the time I get a result back, all links in the chain have been invoked.


Solution

  • Both flags and exceptions can be used, but as you noticed they're not the proper tool.

    Instead, use recursion, like in @IsiahMeadows' answer, or a right fold:

    var makeTest = function (runMoreTests, resolutionTest) {
        return function runTest(result) {
            if (result)
                return result;
            return tryOpenStream(resolutionTest).then(streamToDom).then(runMoreTests);
        };
    }
    
    return Promise.resolve(resolutionTests.reduceRight(makeTest, x => x)(undefined));
    

    or better written as

    var makeTest = function (runMoreTests, resolutionTest) {
        return function runTest() {
            return tryOpenStream(resolutionTest).then(streamToDom).then(result => {
                if (result)
                    return result;
                else
                    return runMoreTests();
            });
        };
    }
    
    return resolutionTests.reduceRight(makeTest, () => Promise.reject("nothing succeeded"))();