Search code examples
ecmascript-6es6-promise

ES6 JS Promises - how to avoid conditional nesting


I am trying to write a piece of code using promises, avoiding nesting them but I am stuck in testing the returned results to handle the promises flow .. Is this pattern workable ??

// set of promise tasks returning values

    function doTask1() => {
        return apiPromise1()
         .then((result1) => {
             return result1;
         })
    }
    function doTask2(result1, paramOne) => {
        return apiPromise2(result1, paramOne)
         .then((result2) => {
             return result2;
         })
    }
    function doTask3(result1) => {
        return apiPromise3()
         .then((result3) => {
             return result3;
         })
    }
    function doTask4(result1, paramOne) => {
        return apiPromise4()
         .then((result4) => {
             return result4;
         })
    }

// main promise to handle the flow of promises according to promises returned results

    function getCurrentProcess(paramOne) {
        const promises = [];

   // how to get the returned result1 to be used by other promises ?
        promises.push(doTask1); 
        if (result1 === 'OK') {
            promises.push(doTask2(result1, paramOne));

            if (result2 === 'OK') {
                promises.push(doTask3(result1));

                if (result3 === 'OK') {
                    promises.push(doTask4(result1, paramOne));
                }
            }
        }

        return Promisz.all(promises)
        .then(() => {
            return 'well done'
        });

    }

// initial calling function

    exports.newJob = functions.https.onRequest((req, res) => {
      const paramOne = { ... }
      getCurrentProcess(paramOne).then((res) => {
        return { status: 200, infos: res };
      }, error => {
        return {status: error.status, infos: error.message};
      }).then(response => {
        return res.send(response);
      }).catch(console.error);
    });

Solution

  • You could write a wrapper function which takes an array of doTaskN as deferred functions:

    const conditional = (...fns) => {
      if(fns.length === 0) return Promise.resolve();
      const [next] = fns;
      return next()
        .then(() => conditional(...fns.slice(1)));
    };
    

    The idea would be to pass in the reference to the doTask functions so that the conditional function executes them. This can be used like:

    conditional(doTask1, doTask2, doTask3, doTask4)
        .then(() => {
          console.log("all done");
        })
        .catch(() => {
          console.log("failed");
        });
    

    Here's a full example of how to use it:

    const conditional = (...fns) => {
      if(fns.length === 0) return Promise.resolve();
      const [next] = fns;
      return next()
      	.then(result => {
          console.log("task:", result);
          if(result === "OK") {
            return conditional(...fns.slice(1))
          }
        });
    };
    
    const task1 = (param1, param2) => Promise.resolve("OK");
    
    const task2 = (param1) => Promise.resolve("OK");
    
    const task3 = () => Promise.resolve("failed");
    
    const task4 = () => Promise.resolve("OK");
    
    conditional(() => task1("one", 2), () => task2(1), task3, task4)
    	.then(() => {
          console.log("all done");
        })
    	.catch(() => {
          console.log("failed");
        });