【问题标题】:ES6 JS Promises - how to avoid conditional nestingES6 JS Promises - 如何避免条件嵌套
【发布时间】:2018-11-13 10:42:56
【问题描述】:

我正在尝试使用 Promise 编写一段代码,避免嵌套它们,但我一直在测试返回的结果以处理 Promise 流.. 这种模式可行吗??

// 一组返回值的承诺任务

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

//主promise根据promise返回结果处理promise流程

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

【问题讨论】:

  • 如果他们的响应不是'OK',你会拒绝这些承诺会更容易。这样你就不需要做任何这些条件了。
  • 感谢您的反馈...在某些 cses 中我需要测试响应 .. 即当响应是列表时,我需要搜索此列表并根据搜索结果执行下一个承诺..

标签: ecmascript-6 es6-promise


【解决方案1】:

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

也许像promise-array-runner 这样的东西会有所帮助?

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

【讨论】:

    【解决方案2】:

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

    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 链:

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

    但是它不会产生可读的代码。

    【讨论】:

      【解决方案3】:

      您可以编写一个包装函数,将 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");
          });

      【讨论】:

        【解决方案4】:
         .then((result1) => {
             return result1;
         })
        

        是一个no-op,应该省略,但我想真正的代码没有这个问题。

        这是async 函数的一个用例,因为它们可以无缝地处理这种控制流,正如另一个答案所暗示的那样。但是由于async 是原始 promise 的语法糖,所以它可以用 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 回调中使用,它可以保存到变量中,而不是通过承诺链传递。

        如果在任务函数中抛出NoResultErrors,Promise 链会变得更简单。

        【讨论】:

          【解决方案5】:

          感谢所有反馈!

          所有答案都是正确的......但是在我的案例中,我投票支持 CodingIntrigue wtapprer 函数解决方案......

          1 - 由于我使用的是 Firebase 函数,它仍然是 ES5,我无法使用同步/等待。仅对 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);
              });
          

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-11-30
          • 2021-05-19
          • 2012-10-05
          • 2013-01-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多