【问题标题】:Node.js handle responses from chained promisesNode.js 处理来自链式承诺的响应
【发布时间】:2018-01-22 07:08:54
【问题描述】:

我有 3 个函数,每个函数都返回一个承诺。如何将每个承诺的响应分配给定义的对象?

这是我的代码:

  const runResponse = {
    webAudits: [],
    webJourneys: [],
    appJourneys: []
  };

  webAuditsFailures(req)
  .then(
    appJourneysFailures(req)
  )
  .then(
    webJourneysFailures(req)
  ).then(
    res.status(201).json({ reports: runResponse })
  );

这是我尝试过的:

webAuditsFailures(req)
  .then(
    (response) => {
      runResponse.webAudits = response
    },
    appJourneysFailures(req)
  )
  .then(
    (response) => {
      runResponse.appJourneys = response
    },
    webJourneysFailures(req)
  ).then(
    (response) => {
      runResponse.webJourneys = response
    },
    res.status(201).json({ reports: runResponse })
  );

但它并没有按预期工作,因为 webAuditsFailures 被再次调用,即使它没有结束,我不明白为什么......

这些是解决此问题的其他失败尝试:

使用await

  const webAudits = await webAuditsFailures(req);
  const appJourneys = await appJourneysFailures(req);
  const webJourneys = await webJourneysFailures(req);

  runResponse.webAudits = webAudits;
  runResponse.webJourneys = webJourneys;
  runResponse.appJourneys = appJourneys;

同样的事情也会发生:

 const webAudits = await webAuditsFailures(req);
 runResponse.webAudits = webAudits;

使用co 模块:

  co(function* () {
    var runResponse = yield {
      webAudits: webAuditsFailures(req),
      webJourneys: appJourneysFailures(req),
      appJourneys: webJourneysFailures(req)
    };
    res.status(201).json({ reports: runResponse });
  });

使用Promise.all

Promise.all([webAuditsFailures(req), appJourneysFailures(req),
  webJourneysFailures(req)])
    .then(function(allData) {
      res.status(201).json({ reports: allData });
  });

这是webAuditsFailures函数,它依次调用另一个返回promise的函数

export default async (req) => {
  const report = req.body.webAudits;
  const def = deferred();

  if(report.length > 0) {
    var reportList = [];
    for(const [reportIndex, item] of report.entries()) {
      for(const [runIndex, run] of item.runs.entries()) {
        const result = await waComplianceBusiness(req, run.id);
        var failureList = [];
        if(result.data.overviews) {
          const compliance = result.data.overviews[0].compliance;
          if(compliance) {
            for(const [index, rule] of compliance.entries()) {
              const response = await waRuleOverview(req, run.id, rule.id);
              const failedConditions = response.data.failedConditions;
              const ruleName = response.data.ruleName;

              if(response.data.pagesFailed > 0) {
                for(const [condIndex, condition] of failedConditions.entries()) {
                  const request = {
                    itemId: condition.conditionResult.id,
                    itemType: condition.conditionResult.idType,
                    parentId: condition.conditionResult.parentId,
                    parentType: condition.conditionResult.parentType
                  }
                  const body = {
                    runId: run.id,
                    ruleId: rule.id,
                    payload: request
                  }

                  waConditionOverview(req, body).done(response => {
                    // do stuff here
                  });
                }
              }
            }
            if(failureList.length > 0) {
              item.runs[runIndex].failures = failureList;
            }
          }
        }
      }
    }
    def.resolve(report);
    return def.promise
  }
  else {
    return [];
  }
}

【问题讨论】:

  • 能否发布 webAuditsFailures() 函数并解释什么是 req?
  • @MarioSantini 我更新了这个问题。 Req 是路由请求
  • 什么是deferred(),为什么不用new Promise()?此外,您还返回了 []def.promise,这可能会使调用代码混淆。您可能想坚持使用一种返回类型。
  • @styfle Deferred 是一个节点模块npmjs.com/package/deferred ...我不使用new Promise(),因为我不知道如何在我的代码中实现它
  • 也许我明白了。您的函数被标记为 async,当您调用该函数时,它会返回一个 Promise。这意味着如果您不使用await,您的代码将继续运行而不是等待。因为它期望你处理承诺,就像你实现 .then() 部分一样。

标签: javascript node.js promise


【解决方案1】:

这就是问题所在:

waConditionOverview(req, body).done(response => {
  // do stuff here
});

您正在执行异步操作,但未等待结果。不要使用延迟模型 - 使用 util.promisify 进行回调。

此外,我强烈建议不要像这样改变请求/响应,而是将信息存储在对象中并返回。

以下是您编写代码的方式:

export default async (req) => {
  const report = req.body.webAudits;  
  if(report.length === 0) return;
  const runs = Array.from(report.entries(), ([i, item]) => item.runs.entries());
  for (const [_, run] of runs) {
    const result = await waComplianceBusiness(req, run.id);
    var failureList = [];
    if (!result.data.overviews) {
      continue;
    }
    const compliance = result.data.overviews[0].compliance;
    if(!compliance) {
      continue;
    }
    for(const [_, rule] of compliance.entries()) {
      const response = await waRuleOverview(req, run.id, rule.id);
      const { failedConditions, ruleName} = response.data;

      if(failureList.length > 0) {
        item.runs[runIndex].failures = failureList;
      }
      if(response.data.pagesFailed === 0) continue;

      for(const [_, condition] of failedConditions.entries()) {
        const request = {
          itemId: condition.conditionResult.id,
          itemType: condition.conditionResult.idType,
          parentId: condition.conditionResult.parentId,
          parentType: condition.conditionResult.parentType
        }
        const body = { runId: run.id, ruleId: rule.id, payload: request}
        const reponse = await waConditionOverview(req, body);
        // do stuff here
        // update response
        // update report, so next time we try it's updated and doesn't return empty;
      }
    }
  }
  return report;
}

【讨论】:

  • 这很完美,但是如果我尝试使用 await (const webAudits = await webAuditsFailures(req); const appJourneys = await appJourneysFailures(req); const webJourneys = await webJourneysFailures(req);) 调用我的所有 3 个函数,那么它将在返回响应后立即重新调用第一个函数 (webAuditsFailures) 并且不会'不要等待其他 2 个函数完成......为什么会这样?
  • 基本上是这样的:调用webAuditsFailures,返回响应,调用appJourneysFailures,调用webAuditsFailures,返回响应appJourneysFailureswebAuditsFailures仍在运行),调用@ 987654331@,为它返回响应,再次为webAuditsFailures 返回响应,然后它再次调用appJourneysFailureswebJourneysFailures 函数......希望这能让你更好地理解这个问题
  • 我发现问题不在于webAuditsFailures函数,因为每次恰好在2分钟后重新调用路由(如果post请求不到2分钟则没有问题)。我在这里发布了一个新问题stackoverflow.com/questions/45876257/…
【解决方案2】:

在一个承诺链中,当前的.then() 应该返回一个承诺。这个promise的结果会传递给下一个.then()

webAuditsFailures(req)
  .then((response) => {
    runResponse.webAudits = response;
    return appJourneysFailures(req); // return a promise
  })
  .then((response) => { // response contains the result of the promise
    runResponse.appJourneys = response;
    return webJourneysFailures(req);
  })
  .then((response) => {
    runResponse.webJourneys = response;
    res.status(201).json({ reports: runResponse });
  });

根据最后一个.then() 中的.json() 所做的事情,如果promise 链中还有其他.then()s,您也应该返回它。

【讨论】:

  • 使用这种方法会导致同样的问题...函数webAuditsFailures 被再次调用,即使它没有返回任何内容
猜你喜欢
  • 1970-01-01
  • 2017-01-22
  • 2021-04-10
  • 1970-01-01
  • 2019-09-25
  • 1970-01-01
  • 1970-01-01
  • 2017-04-03
  • 1970-01-01
相关资源
最近更新 更多