如何绕过异步Node.js行为?

时间:2015-03-08 18:09:13

标签: javascript node.js asynchronous

我正在开发一个ping网站并在Web UI中返回结果的脚本。但是,我遇到了一个问题,我正试图找出最佳解决方案。

这段代码需要返回一个状态数组,但是由于Node.js的异步行为,它返回一个空数组,因为代码需要时间来执行。

这就是我所拥有的:

var ping = require('ping');

function checkConnection(hosts) {

    var results = [];

    hosts.forEach(function (host) {
        ping.sys.probe(host, function (isAlive) {
            results.push({"host": host, "status": isAlive});
        });
    });

    return {results: results, timestamp: new Date().getTime()};
}

module.exports.checkConnection = checkConnection;

我知道您可以使用计时器来解决这个问题,但这里的简单和最理想的解决方案是什么?

2 个答案:

答案 0 :(得分:5)

  

如何解决异步Node.js行为?

别。而是通过让checkConection接受回调或返回承诺来拥抱

回调示例:

function checkConnection(hosts, callback) {

    var results = [];

    hosts = hosts.slice(0); // Copy
    hosts.forEach(function (host) {
        ping.sys.probe(host, function (isAlive) {
            results.push({"host": host, "status": isAlive});
            if (results.length === hosts.length) {
                callback({results: results, timestamp: new Date().getTime()});
            }
        });
    });
}

请注意hosts的防御性浅拷贝。如果你不这样做,那么由于这段代码是异步运行的,调用代码可以在处理响应时添加到hosts数组或从function checkConnection(hosts, callback) { var results = []; var requests = hosts.length; hosts.forEach(function (host) { ping.sys.probe(host, function (isAlive) { results.push({"host": host, "status": isAlive}); if (results.length === requests) { callback({results: results, timestamp: new Date().getTime()}); } }); }); } 数组中删除,并且长度永远不会匹配。

另一种无需复制即可处理此问题的方法是简单计算您已启动的请求数量:

hosts

看起来像它会设置竞争条件(如果在设置requests之后但在您启动probe之前修改了hosts的内容会怎样?查询?)但它没有,因为Node在一个线程上运行你的JavaScript,所以没有其他代码可以进入并修改requests = hosts.lengthhosts.forEach行之间的{{1}}。

答案 1 :(得分:1)

像T.J.如果要在node.js中编程,则需要接受异步行为,因为这是它如何工作的基本原则以及如何使用node.js对响应性,可伸缩的服务器进行编码。

T.J.的答案是解决这一特定问题的简单方法。但是,由于异步问题会在node.js中反复出现,因此promises可以成为管理异步行为的非常有用的工具,它们很快就成为具有强大错误处理功能的更复杂的多操作序列所必不可少的。

所以,这是使用Promises编码问题的解决方案:

var ping = require('ping');
var Promise = require('bluebird');

// make a version of ping.sys.probe that returns a promise when done
ping.sys.probeAsync = function(host) {
    return new Promise(function(resolve, reject) {
        ping.sys.probe(host, function(isAlive) {
            resolve({"host": host, "status": isAlive});
        });
    }
}

function checkConnection(hosts) {
    var promises = hosts.map(function(host) {
        return ping.sys.probeAsync(host);
    });
    return Promise.all(promises).then(function(results) {
        return {results: results, timestamp: new Date().getTime()};
    });
}

module.exports.checkConnection = checkConnection;

样本用法:

myModule.checkConnection(myArrayOfHosts).then(function(results) {
    // results is the {results: results, timestamp: time} object
});

一步一步,这是如何工作的:

  1. 加载Bluebird promise库。

  2. 创建一个名为ping.sys.probe的{​​{1}}的预授权版本,该版本会返回一个承诺,该承诺将在底层调用完成时解析。

  3. 在数组上使用ping.sys.probeAsync,在数组中的每个项目上调用.map()创建一个promises数组。

  4. 使用ping.sys.probeAsync创建一个新的promise,它是数组中所有promise的聚合。只有当数组中的所有promise都已解决时(例如已完成),它才会调用它的Promise.all()处理程序。

  5. .then()添加.then()处理程序,以便将时间戳添加到结果中。

  6. 返回Promise.all()承诺,以便Promise.all()的来电者获得可以使用的承诺。

  7. 调用checkConnection()时,请使用checkConnection()处理程序知道所有操作何时完成并获取结果。


  8. 希望您可以看到,一旦您拥有了函数的promisified版本并且了解promises如何工作,您就可以更加简单地编写实际的异步代码。并且,如果你还有错误处理或者必须一个接一个地运行的异步操作序列(这里没有你的东西),使用promises的优势就更大了。


    P.S。我认为Bluebird的.then()可用于将Promise.map()hosts.map()合并为一个调用,但我自己没有使用过该函数,所以我没有在这里提供它。