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

时间:2021-10-22 11:47:56

In my code, based on a specific condition, I would like to skip to the done function, irrespective of all the then functions.

在我的代码中,基于特定条件,我想跳过done函数,而不管所有函数如何。

The original version of this question is in the edits. The following is the actual problem I am dealing with. Sorry for the inconvenience

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

Actual Problem :

实际问题:

I am reading a file and processing it. If the contents of the file match certain conditions, I have to do a series of operations on the file system (say read and write few files) and then to execute done function. If the conditions fail, I have to skip all the series of operations and I have to execute the done function directly.

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

I return an object (lets say result) in all the then functions and in the next then I update result and return it. So, when all the then are done, done will have the accumulated result. Finally, the done will process result and print it.

我在所有的函数中返回一个对象(比如说结果),然后我更新结果并返回它。因此,当完成所有操作后,完成将获得累积的结果。最后,完成将处理结果并打印出来。

So, if the conditions are not met initially, done will simply print result (which would be empty).

因此,如果最初不满足条件,则只需打印结果(这将是空的)。

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 个解决方案

#1


23  

It depends a bit on what the conditions to skip are, what kind of operations you are doing, and how “useful” the whole thing is when the conditions failed. You might be able to use a smart rejection here to bring the message across. Otherwise, I believe the correct way to deal with this is really a nested set of promise calls.

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

This also matches the core idea behind promises, which is to bring back synchronous control structures to asynchronous code execution. In general, when using promises, you should first think about how you would do the task with synchronous code. And if you think about your situation, it would probably work like this:

这也符合promises背后的核心思想,即将同步控制结构带回异步代码执行。通常,在使用promises时,首先应该考虑如何使用同步代码完成任务。如果你考虑一下你的情况,它可能会像这样工作:

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

So you would have a real branch in there in synchronous code. As such, it makes no sense that you would want to avoid that in asynchronous code using promises. If you could just skip things, you were essentially using jumps with gotos. But instead, you branch off and do some other things separately.

所以你会在同步代码中有一个真正的分支。因此,使用promises在异步代码中避免使用它是没有意义的。如果你可以跳过的东西,你实际上是使用带有gotos的跳转。但相反,你分开并分开做其他事情。

So going back to promises and asynchronous code, having an actual branch with another set of chained operations is completely fine, and actual in spirit of the intent behind promises. So above code could look like this:

因此,回到promises和异步代码,拥有另一组链接操作的实际分支是完全正确的,并且实际上是承诺背后的意图。所以上面的代码看起来像这样:

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

Also note, that the promise pipeline automatically looks a lot more cleaner when you start using functions that return promises on their own, instead of creating them inline from then.

另请注意,当您开始使用自己返回promises的函数时,promise管道会自动看起来更清晰,而不是从那时开始内联它们。

#2


9  

But then we are nesting the thenable functions. This is what we wanted to avoid in the first place using promises.

但后来我们正在嵌套那些可靠的函数。这是我们想要首先使用promises避免的。

No, this is indeed the proper way. For branching, you will always need an additional level of nesting. If indentation is getting too large, you still can apply the old trick of calling sub-procedures (which is also used for unnesting callback functions).

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


Other solutions are quite ugly. Skipping a few then-handlers in a chain, without deeper nesting, can be done by throwing an exception and rejecting the promise; this could be caught in the end then. It might apply to a few scenarios, but I wouldn't consider this a good idea.

其他解决方案非常难看。在没有更深层嵌套的情况下跳过链中的一些后处理程序,可以通过抛出异常并拒绝承诺来完成;这可能会在最后被抓住。它可能适用于几种情况,但我不认为这是一个好主意。

The other way I could think of would be wrapping the conditional result in another data structure, which could be passed through the chain of thens. This would be like Maybe in Haskell or Option in Scala, and you would map over them in the handlers. However, this would also require an additional level of nesting, would be less efficient to explicitly propagate nothing through the chain, and would have problems with returned promises in the chain.

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

#3


1  

If I understand "skip" correctly, then the generic solution is not to return a value under "skip" conditions, thus allowing the input value to pass through transparently.

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

eg:

例如:

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

#4


0  

I use a context variable and implement the conditional logic in functions called within the promise chain. Each function returns the same context variable. This keeps the promise chain tidy. Using function names that hint at the conditional tests improves readability.

我使用上下文变量并在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);
    });
}

#1


23  

It depends a bit on what the conditions to skip are, what kind of operations you are doing, and how “useful” the whole thing is when the conditions failed. You might be able to use a smart rejection here to bring the message across. Otherwise, I believe the correct way to deal with this is really a nested set of promise calls.

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

This also matches the core idea behind promises, which is to bring back synchronous control structures to asynchronous code execution. In general, when using promises, you should first think about how you would do the task with synchronous code. And if you think about your situation, it would probably work like this:

这也符合promises背后的核心思想,即将同步控制结构带回异步代码执行。通常,在使用promises时,首先应该考虑如何使用同步代码完成任务。如果你考虑一下你的情况,它可能会像这样工作:

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

So you would have a real branch in there in synchronous code. As such, it makes no sense that you would want to avoid that in asynchronous code using promises. If you could just skip things, you were essentially using jumps with gotos. But instead, you branch off and do some other things separately.

所以你会在同步代码中有一个真正的分支。因此,使用promises在异步代码中避免使用它是没有意义的。如果你可以跳过的东西,你实际上是使用带有gotos的跳转。但相反,你分开并分开做其他事情。

So going back to promises and asynchronous code, having an actual branch with another set of chained operations is completely fine, and actual in spirit of the intent behind promises. So above code could look like this:

因此,回到promises和异步代码,拥有另一组链接操作的实际分支是完全正确的,并且实际上是承诺背后的意图。所以上面的代码看起来像这样:

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

Also note, that the promise pipeline automatically looks a lot more cleaner when you start using functions that return promises on their own, instead of creating them inline from then.

另请注意,当您开始使用自己返回promises的函数时,promise管道会自动看起来更清晰,而不是从那时开始内联它们。

#2


9  

But then we are nesting the thenable functions. This is what we wanted to avoid in the first place using promises.

但后来我们正在嵌套那些可靠的函数。这是我们想要首先使用promises避免的。

No, this is indeed the proper way. For branching, you will always need an additional level of nesting. If indentation is getting too large, you still can apply the old trick of calling sub-procedures (which is also used for unnesting callback functions).

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


Other solutions are quite ugly. Skipping a few then-handlers in a chain, without deeper nesting, can be done by throwing an exception and rejecting the promise; this could be caught in the end then. It might apply to a few scenarios, but I wouldn't consider this a good idea.

其他解决方案非常难看。在没有更深层嵌套的情况下跳过链中的一些后处理程序,可以通过抛出异常并拒绝承诺来完成;这可能会在最后被抓住。它可能适用于几种情况,但我不认为这是一个好主意。

The other way I could think of would be wrapping the conditional result in another data structure, which could be passed through the chain of thens. This would be like Maybe in Haskell or Option in Scala, and you would map over them in the handlers. However, this would also require an additional level of nesting, would be less efficient to explicitly propagate nothing through the chain, and would have problems with returned promises in the chain.

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

#3


1  

If I understand "skip" correctly, then the generic solution is not to return a value under "skip" conditions, thus allowing the input value to pass through transparently.

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

eg:

例如:

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

#4


0  

I use a context variable and implement the conditional logic in functions called within the promise chain. Each function returns the same context variable. This keeps the promise chain tidy. Using function names that hint at the conditional tests improves readability.

我使用上下文变量并在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);
    });
}