在 Q Promises 中跳过 then 函数的正确方法

IT技术 javascript node.js promise q
2021-02-21 18:58:45

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

这个问题的原始版本在编辑中。以下是我正在处理的实际问题。带来不便敬请谅解

实际问题:

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

result在所有then函数中返回一个对象(可以说),然后在下一次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);
});
4个回答

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

这也符合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.

但是我们正在嵌套 thenable 函数。这是我们首先想要避免使用 Promise 的。

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


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

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

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

例如:

...then(function (randomInteger) {
    var remainder = randomInteger % 2;
    console.log(['Even','Odd'][remainder] + ' number: ', randomInteger);
    if(remainder) {
        return randomInteger + 1;
    }
})...
是的,您需要在每个.then()成功处理程序中应用一些“跳过”逻辑这就是.then()链条的本质
2021-04-19 18:58:45
但这仍然会执行下一个then函数,对吗?唯一的区别是, arguments 是{"0": undefined}
2021-04-21 18:58:45
当然,您始终可以在后续.then()s 中使用带有空错误处理程序的失败路由,但最终处理程序(对于您的示例中的奇数)需要是失败处理程序(即.fail(fn).then(null, fn))。
2021-04-21 18:58:45
不不undefined,正如我所说,如果没有返回,则传递输入值。
2021-04-30 18:58:45

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

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