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

Posted

技术标签:

【中文标题】在 Q Promises 中跳过 then 函数的正确方法【英文标题】:Proper way to skip a then function in Q Promises 【发布时间】: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 这将使下一个处理功能非常依赖于上一步。 你能用一个更真实的问题吗(也许你的实际任务,有很多伪代码会发生什么)?您的同步示例并没有多大意义。 【参考方案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);
    );

【讨论】:

以上是关于在 Q Promises 中跳过 then 函数的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

Q-promises 和错误处理

如何在纯函数中跳过不必要的 IO?

我正在尝试实现一个函数,该函数接受数组输入并给出数组的乘积,在 Javascript 中跳过 0 但我只能使用 .reduce

在假设中跳过伪造的例子

有啥函数,啥可以获取当天的第一个值并在 Python 中跳过同一天的值?

在 Yii2 中跳过 CSV 文件的第一行