在Node中有条件地调用异步函数

7

我有以下示例代码 - 第一部分可能会导致异步调用,也可能不会 - 无论哪种情况都应该继续。由于需要在条件为false时运行,因此无法将其余代码放在异步回调中。那么如何做到这一点?

if(condition) {
    someAsyncRequest(function(error, result)) {
        //do something then continue
    }
}

 //do this next whether condition is true or not

我假设把下面的代码放到一个函数里,然后在上面的异步调用中调用该函数,或者在else语句中调用该函数(如果条件为false)可能是可行的——但是否有不需要将其拆分成函数的替代方法呢?


Async-await包可能会有所帮助(允许以同步风格编写异步代码,支持异常等)。https://www.npmjs.com/package/asyncawait - 这种语法在未来的Node版本中也将得到本地支持,请参见https://github.com/nodejs/promises/issues/4。 - Maxime Pacary
4个回答

7
我经常在Node中使用的一个库是Async (https://github.com/caolan/async)。据我所知,这个库也支持浏览器,因此您应该能够在分发时使用npm / concat / minify。如果您只在服务器端使用它,请考虑使用https://github.com/continuationlabs/insync,这是Async的稍微改进版,已删除了一些浏览器支持。
当使用条件异步调用时,我常用的一种常见模式是将我想要按顺序使用的函数填充到数组中,并将其传递给async.waterfall。
下面是一个示例:
var tasks = [];

if (conditionOne) {
    tasks.push(functionOne);
}

if (conditionTwo) {
    tasks.push(functionTwo);
}

if (conditionThree) {
   tasks.push(functionThree);
}

async.waterfall(tasks, function (err, result) {
    // do something with the result.
    // if any functions in the task throws an error, this function is 
    // immediately called with err == <that error>
});

var functionOne = function(callback) {
    // do something
    // callback(null, some_result);
};

var functionTwo = function(previousResult, callback) {
    // do something with previous result if needed
    // callback(null, previousResult, some_result);
};

var functionThree = function(previousResult, callback) {
    // do something with previous result if needed
    // callback(null, some_result);
};

当然,您可以使用 promises。无论哪种情况,我都喜欢使用 async 或 promises 来避免嵌套回调。通过不使用嵌套回调,您可以避免变量冲突、提升错误、向右“行进”、难以阅读的代码等问题。

1
FWIW,异步操作是第一个反模式的替代方案: http://webapplog.com/seven-things-you-should-stop-doing-with-node-js/ - zedd45

3

这只是另一种实现方式,这是我如何抽象出这个模式的。可能有一些库(Promise?)可以处理同样的事情。

function conditional(condition, conditional_fun, callback) {
    if(condition)
        return conditional_fun(callback);
    return callback();
}

在代码中,您可以编写以下内容:
conditional(something === undefined,
            function(callback) {
               fetch_that_something_async(function() {
                  callback();
               });
            },
            function() {

                       /// ... This is where your code would continue


             });

3
只需声明其他需要运行的函数,即可在需要时运行该函数:
var otherFunc = function() {
   //do this next whether condition is true or not
}

if(condition) {
    someAsyncRequest(function(error, result)) {
        //do something then continue

        otherFunc();
    }
} else {
    otherFunc();
}

4
是的,正如我之前提到的那样,这就是我现在拥有的 - 只是想知道是否有一种避免将代码封装在函数中的方法。 - cyberwombat
@Yashua,不行,因为你的 someAsyncRequest 是异步的,你无法保证它何时被执行,而且由于JavaScript本身不是线程的,你不能仅仅“暂停”程序的某一部分,直到某个异步调用已经完成。因此,你需要将代码中要延迟执行的部分保护在一个函数内,并在适当的时间调用它(即立即调用或仅在异步代码执行完成后才调用)。 - Yanick Rochon

0
我建议使用ClojureScript,它有一个很棒的core-async库,可以在处理异步调用时使生活变得超级简单。
在你的情况下,你可以写出类似这样的代码:
(go
  (when condition
    (<! (someAsyncRequest)))
  (otherCodeToHappenWhetherConditionIsTrueOrNot))

请注意go宏,它将使主体异步运行,以及<!函数,它将阻塞直到异步函数返回。由于<!函数位于when条件内部,只有在条件为真时才会阻塞。

@kleopatra 请查看我对答案的修改。希望现在更好了。 - Asher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接