【问题标题】:Proper way to skip a then function in Q Promises在 Q Promises 中跳过 then 函数的正确方法
【发布时间】:2014-03-01 20:40:04
【问题描述】:

在我的代码中,根据特定条件,我想跳到done 函数,而不考虑所有then 函数。

这个问题的原始版本在编辑中。以下是我正在处理的实际问题。很抱歉给您带来不便

实际问题:

我正在读取一个文件并处理它。如果文件的内容符合某些条件,我必须对文件系统进行一系列操作(比如读写几个文件),然后执行done函数。如果条件不成立,我要跳过所有的一系列操作,直接执行done函数

我在所有then 函数中返回一个对象(比如说result),在接下来的then 中我更新result 并返回它。因此,当所有then 完成后,done 将具有累积的result。最后done会处理result并打印出来。

因此,如果最初不满足条件,done 将简单地打印result(将为空)。

Q()
.then(readFile)
.then(function (contents) {
    var processResult = process the contents;
    if (processResult) {
        return {};
    } else {
        // How can I skip to `done` from here
    }
})
.then(function (results) {
    // do some more processing and update results
    return results;
})
...   // Few more then functions similar to the one above
...
.fail(function (exception) {
    console.error(exception.stack);
})
.done(function (results) {
   // do some more processing and update results
   console.log(results);
});

【问题讨论】:

  • 虽然不确定,但你不能返回一个包含布尔值的对象吗,根据这个布尔值,你的下一个函数将执行是/否。 return { randomNumber : ..., process : true };,在您的下一个电话中:.then(function (data) { if(!data.process) return; return data.randomNumber + 1; })
  • @DieterGoetelen 这将使下一个处理功能非常依赖于上一步。
  • 你能用一个更真实的问题吗(也许你的实际任务,有很多伪代码会发生什么)?您的同步示例并没有多大意义。

标签: javascript node.js promise q


【解决方案1】:

这在一定程度上取决于要跳过的条件是什么,您正在执行什么样的操作,以及当条件失败时整个事情有多“有用”。您也许可以在此处使用智能拒绝来传达信息。否则,我相信处理这个问题的正确方法实际上是一组嵌套的 promise 调用。

这也匹配core idea behind promises,它是将同步控制结构带回异步代码执行。一般来说,在使用 Promise 时,您应该首先考虑如何使用同步代码完成任务。如果你考虑一下你的情况,它可能会这样工作:

var contents = readFromFile();
var results = initialOperation(contents);
if (fancyCondition(results)) {
     results = doSomething(results);
     results = doMore(results);
}
processAndPrint(results);

所以你会有一个真正的同步代码分支。因此,在使用 Promise 的异步代码中避免这种情况是没有意义的。如果你可以跳过 的东西,你基本上是在使用 goto 跳转。但相反,您分叉并单独做一些其他事情。

所以回到 Promise 和异步代码,拥有一个带有另一组链式操作的实际分支是完全没问题的,并且符合 Promise 背后的意图。所以上面的代码可能是这样的:

readFromFile(fileName)
.then(initialOperation)
.then(function (results) {
    if (fancyCondition(results) {
        return doSomething(results)
            .then(doMore);
    }
    return results;
})
.catch(errorHandler)
.then(processResults)
.then(outputResults); // Or `done` in Q

另请注意,当您开始使用自行返回 Promise 的函数时,Promise 管道会自动看起来更干净,而不是从 then 内联创建它们。

【讨论】:

    【解决方案2】:

    然后我们嵌套了 thenable 函数。这是我们首先要使用 Promise 来避免的。

    不,这确实是正确的方法。对于分支,您总是需要额外的嵌套级别。如果缩进太大,你仍然可以使用调用子过程的老技巧(这也用于取消嵌套回调函数)。


    其他解决方案相当难看。跳过链中的几个then-handlers,没有更深的嵌套,可以通过抛出异常并拒绝promise来完成;这最终可能会被抓住。它可能适用于一些场景,但我认为这不是一个好主意。

    我能想到的另一种方法是将条件结果包装在另一个数据结构中,它可以通过thens 的链传递。这就像 Haskell 中的 Maybe 或 Scala 中的 Option 一样,您可以在处理程序中使用 map 覆盖它们。但是,这也需要额外的嵌套级别,在链中显式传播任何内容的效率会降低,并且在链中返回的 Promise 会出现问题。

    【讨论】:

      【解决方案3】:

      如果我正确理解“跳过”,那么通用解决方案是不在“跳过”条件下返回值,从而允许输入值透明地通过。

      例如:

      ...then(function (randomInteger) {
          var remainder = randomInteger % 2;
          console.log(['Even','Odd'][remainder] + ' number: ', randomInteger);
          if(remainder) {
              return randomInteger + 1;
          }
      })...
      

      【讨论】:

      • 但这仍然会执行下一个then函数,对吧?唯一的区别是,参数是{"0": undefined}
      • 是的,您需要在每个.then() 成功处理程序中应用一些“跳过”逻辑。这就是.then() 链的本质。
      • 不,不是undefined,正如我所说,如果没有返回,则传递输入值。
      • 当然,您始终可以在随后的.then()s 中使用带有空错误处理程序的失败路由,但最终的处理程序(在您的示例中为奇数)需要是一个失败处理程序(即.fail(fn).then(null, fn))。
      【解决方案4】:

      我使用上下文变量并在承诺链中调用的函数中实现条件逻辑。每个函数都返回相同的上下文变量。这使承诺链保持整洁。使用暗示条件测试的函数名称可以提高可读性。

      function processFirstPass(context) {
          var processResult = process the contents;
          if (processResult) {
              context.result = processResult;
          }
          context.specialTag = ! processResult;
          return context;
      }
      
      processForOnlySpecialTag(context) {
          if (context.specialTag) {
              context.result = // do some more processing and update results
          }
          return context;
      }
      
      function process() {
          var context = { filename: 'input.txt' };
          readFromFile(context)
          .then(procesFirstPass)
          .then(processForOnlySpecialTag)
          .fail(function (exception) {
              console.error(exception.stack);
          })
          .done(function (context) {
             // do some more processing and update results
             console.log(context.result);
          });
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-05
        相关资源
        最近更新 更多