ES6 JS Promises - 如何避免条件嵌套

时间:2018-06-04 06:48:19

标签: ecmascript-6 es6-promise

我正在尝试使用promises编写一段代码,避免嵌套它们但我仍然在测试返回的结果来处理promises流程。 这种模式是否可行?

//一组承诺任务返回值

    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;
         })
    }

//根据承诺返回结果处理承诺流程的主要承诺

    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'
        });

    }

//初始调用功能

    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);
    });

5 个答案:

答案 0 :(得分:1)

如果您想以更多程序方式编写承诺,则需要使用async/await(ES6)。如果您需要向后兼容ES5,则需要使用将await / async转换为ES5的babel或typescript。

async function getCurrentProcess(paramOne) {
    const result1 = await doTask1(); 
    if (result1 === 'OK') {
        const result2 = await doTask2(result1, paramOne);

        if (result2 === 'OK') {
            const result3 = await doTask3(result1);

            if (result3 === 'OK') {
                await doTask4(result1, paramOne);
            }
        }
    }

    return 'well done'

}

如果没有async / await,您需要使用promise chain:

doTask1().then((result1)=>{
   if (result1 === 'OK') {
      ...
   }
   ...
})

然而,它不会产生可读代码。

答案 1 :(得分:0)

如果你希望你的promise返回结果被其他promises使用,你不应该使用Promise.all()方法,因为它不按你想要的顺序运行方法,它只是等待所有的promise方法完成并返回所有结果。

promise-array-runner这样的东西会有帮助吗?

也许您可以检查任务方法中是否result === 'OK'?或者创建一个Factory来处理这个问题。

答案 2 :(得分:0)

您可以编写一个包装函数,该函数将doTaskN数组作为延迟函数:

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

我们的想法是传递对doTask函数的引用,以便conditional函数执行它们。这可以像:

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

以下是如何使用它的完整示例:

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");
    });

答案 3 :(得分:0)

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

是一个无操作,应该省略,但我认为真正的代码没有这个问题。

这是async函数的一个用例,因为它们可以无缝地处理这种控制流,正如另一个答案所暗示的那样。但由于async是原始承诺的语法糖,因此可以用ES6编写。由于任务取决于彼此的结果,因此无法使用Promise.all处理它们。

这与this one that uses async相同。

您可以通过抛出异常来避免承诺链,并避免使用以下嵌套条件:

// should be additionally handled if the code is transpiled to ES5
class NoResultError extends Error {}

function getCurrentProcess(paramOne) {
    doTask1()
    .then(result1 => {
      if (result1 !== 'OK') throw new NoResultError(1);
      return result1;
    })
    .then(result1 => ({ result1, result2: doTask2(result1, paramOne) }))
    .then(({ result1, result2 }) => {
      if (result2 !== 'OK') throw new NoResultError(2);
      return result1;
    })
    // etc
    .then(() => {
        return 'well done';
    })
    .catch(err => {
      if (err instanceof NoResultError) return 'no result';
      throw err;
    })
}

由于result1用于多个then回调,因此可以将其保存到变量中,而不是通过promise链传递。

如果在任务函数中抛出NoResultError,则Promise链可能变得更简单。

答案 4 :(得分:0)

感谢所有反馈!!

所有答案都是权利......但是我投票支持CodingIntrigue wtapprer功能解决方案......

1 - 当我使用Firebase功能时,它仍然是ES5,我不能使用sync / await。仅对Firebase函数使用babel或typescript将导致更多的设置工作...

2 - 我测试了各种用例,这个模式很容易用JS级别来理解......我觉得以后可以改进...

所以我终于开始了......

    let auth = null;
    let myList = null;

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

    const task1 = (param1) => Promise.resolve()
        .then(() => {
        console.log('TASK1 executed with params: ', param1)
        auth = "authObject"
          return true;
        });

    const task2 = (param1, param2) => Promise.resolve()
        .then(() => {
        console.log('TASK2 executed with params: ', param1, param2)
          return true;
        });

    const task3 = (param1, param2) => Promise.resolve()
        .then(() => {
        console.log('TASK3 executed with params: ', param1, param2)
        myList = "myListObject"
        console.log('search for param2 in myList...')
        console.log('param2 is NOT in myList task4 will not be executed')
          return false;
        });

    const task4 = (param1) => Promise.resolve()
        .then(() => {
        console.log('TASK4 executed with params: ', param1)
          return true;
        });

    // FIREBASE HTTP FUNCTIONS ==================
    exports.newContactMessage = functions.https.onRequest((req, res) => {
      conditional(() => task1("senderObject"), () => task2(auth, "senderObject"), () => task3(auth, "senderObject"), () => task4("senderObject"))
        .then((res) => {
        return { status: 200, infos: res };
      }, error => {
        return {status: error.status, infos: error.message};
      }).then(response => {
        return res.send(response);
      }).catch(console.error);
    });