【问题标题】:Conditional call to second async function, based on data collected before the first async function根据在第一个异步函数之前收集的数据,对第二个异步函数进行条件调用
【发布时间】:2017-04-03 06:56:34
【问题描述】:

我在数组中有一些数据。

let data = ['firstData', 'secondData'];

第一个对象(firstData)始终存在,secondData 可以为空。所以我总是用它来调用我的异步函数,比如:

myAsyncFunction(data[0]).then(result => {
    //do something here
});

如果data[1] 存在,我想用data[1] 调用myAsyncFunction

我目前的解决方案是嵌套承诺。像这样:

myAsyncFunction(data[0]).then(result => {
    return result;
}).then(result => {
    if(data[1] !== null){
        myAsyncFunction(data[1].then(resultTwo => {
        //dostuff with data
      });
    }
});

我不是很喜欢这个解决方案,但它确实有效。这一定是一个更好的方法。 result 始终是一个对象。我试过Promise.all,但它不起作用。我认为这是某种“竞争条件”的问题(我在这里如履薄冰)。

我目前的解决方案是唯一的吗?

这是 myAsuncFunction 的样子:

myAsyncFunction(personData){
    return new Promise<any> ((resolve, reject) => {

        //Assingen private variables

    secondAsyncFunction(somePersonData).then(result =>
     {

                    //Do some sync stuff

          return someNewData; //not a promise
     })
         .then(thirdAsyncFunction)
         .then(data => {
              resolve(data);
         })
         .catch(error => {
          reject(error);
     });
});

【问题讨论】:

  • 所以我有几个问题...您的 if 语句正在检查它是否不等于 null...这是否意味着您的数组可能是 data = ["firstData", null]?此外,您可能想尝试使用 Promise.map 做一些事情。 bluebirdjs.com/docs/api/promise.map.html
  • 嗨!正确,data[1] 有时可能为空。我没有使用蓝鸟,我使用的是 ES6 承诺。我认为有区别(?)。
  • 如果您只是想知道它们何时全部完成,并且它们可能为空,您可以像这样从数据数组创建一个映射......让 data_promises = data. map(function(data){ return data ? myAsyncFunction(data) : Promise.resolve(); });然后调用 Promise.all(data_promises);。这几乎可以检查值是否存在(null 当然是错误的),并且会创建一个 Promise,如果它是 null 则立即解析
  • 如果您可以分享您尝试Promise.all 时的错误消息或代码,我们将不胜感激。如果存在竞争条件,这是否意味着第一个承诺必须在第二个承诺开始之前完成?
  • 我没有收到任何错误消息。但是我在 Promise.all 中得到了两个对象。问题是两个对象都基于来自第二个对象(data[1])的数据。

标签: javascript node.js asynchronous es6-promise


【解决方案1】:

Promise.all 设计用于在数组中有动态数量的 Promise 时使用,并且您希望将它们组合成一个 Promise,该 Promise 在数组中的所有 Promise 都已解析时解析。

根据你的用例,你应该可以这样使用它:

data = data.filter(x => x !== null);

Promise.all(data.map(myAsyncFunction)).then((results) => {
    const [resultOne, resultTwo] = results;
    if (resultTwo) {
        // handle resultTwo
    }
    // handle resultOne
});

【讨论】:

  • 当我使用 Promise.all 时,我只得到基于第二个值的结果。正如我所说,我认为这与比赛条件有关。
  • 在这种情况下,您可能会遇到 Promise 泄漏(即您的异步函数在实际执行完成之前自行解析/拒绝
  • 哦...这可以解释。找出是否存在任何承诺泄漏的最佳方法是什么?
  • @4castle,你可能想做data.filter(i=&gt;i).map(myAsyncFunction)。因为myAsyncFunction 不应该在data[x]null 时执行
  • 没有看到函数体我不能肯定地说,但是 NO Promise.all 将始终执行 所有 里面的承诺,并且 将返回 i> 他们已解决的回调。除非它们中的任何一个 rejectthrow 出现异常,在这种情况下,Promise.all 将在第一次失败时拒绝,并且不会报告任何成功(pendingcompleted
【解决方案2】:

鉴于data 在承诺结果之前是静态已知的,您不需要将条件放在then 回调中 - 您可以使整个链接成为条件:

var promise = myAsyncFunction(data[0]);
if(data[1] !== null){
    promise.then(result =>
        myAsyncFunction(data[1])
    ).then(resultTwo => {
        //dostuff with data
    });
}

一般来说,注意嵌套并不是一件坏事,当你想要分支控制流时需要它。只是不要忘记始终 return 您对所有(回调)函数的承诺。

【讨论】:

    【解决方案3】:

    你可以这样做:

    const stuffOnlyRelatedToSecondData = result2 => /*do something*/
    
    myAsyncFunction(data[0]).then(result => {
        if (data[1] === null) {return null}
        return myAsyncFunction(data[1]).then(stuffOnlyRelatedToSecondData) 
    }).then(/*Anything to do after [1] or [1,2]*/)

    这样你可以使用相同的链,两种可能的情况如下:

    myAsyncFunction(data[0]) --> END
    myAsyncFunction(data[0]) --> myAsyncFunction(data[1]) --> END
    

    编辑:

    似乎myAsyncFunction 可能存在承诺泄漏,请尝试使用类似的东西:

    const myAsyncFunction = personData => {
        //Assigning private variables
        return secondAsyncFunction(somePersonData).then(result => {
            //Do some sync stuff
            return someNewData; //not a promise
        })
        .then(thirdAsyncFunction)
    }
    

    我们可以这样写,因为secondAsyncFunction 启动了一个新的 Promise,因此我们不需要另一个 Promise 包装器。并且可以将 catch 放入执行 myAsyncFunction 的父 Promise 链本身中

    【讨论】:

    • 我尝试了编辑中的代码。我知道再次包装它的方式是不必要的。但问题是 secondAsyncFunction 用相同的值调用了两次。即使在返回 secondAsyncFunction 之前所有值都是正确的。
    • 嗯。我在返回 secondAsyncFunction 之前创建了一个变量(我们称之为 y)。如果我 console.log(y) (在 secondAsyncFunction 内)我得到属性 .prop 的正确值(例如)。但是当我 console.log(y.prop) - 我得到错误的值?
    • @mintermasher 听起来您需要进行更多调试,直到您可以使用实际代码将问题缩小到minimal reproducible example
    猜你喜欢
    • 2022-01-07
    • 2019-02-12
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    • 1970-01-01
    • 1970-01-01
    • 2021-06-29
    • 2021-10-06
    相关资源
    最近更新 更多