如何处理为nodejs中的每个元素应用异步函数的大数组?

时间:2015-12-10 00:42:55

标签: node.js zombiejs

我正在使用zombie.js来抓取一个网站,我必须使用回调样式连接到每个网址。关键是我有一个urls数组,我需要使用异步函数处理每个url。这是我的第一个方法:

Array urls = {http..., http...};
function process_url(index)
{
   if(index == urls.length)
      return;

   async_function(url, 
                  function() { 
                        ... 
                        //parse the url 
                        ...
                        // Process the next url
                        process_url(index++);
                       }
                   );
}

process_url(0)

不使用某人第三方nodejs库将asyn功能用作同步功能或等待功能(wait.for,synchornized,mocha),这就是我解决这个问题的方法,我不会&#39 ;知道如果阵列太大会发生什么。调用下一个函数时,是否从内存中释放了函数?或者所有的功能都在内存中直到结束?

有什么想法吗?

1 个答案:

答案 0 :(得分:5)

您的计划将有效。我称之为“手动排序异步操作”。

您正在做的事情的通用版本如下所示:

function processItem(data, callback) {
    // do your async function here
    // for example, let's suppose it was an http request using the request module
    request(data, callback);
}

function processArray(array, fn) {
    var index = 0;

    function next() {
        if (index < array.length) {
            fn(array[index++], function(err, result) {
                // process error here
                if (err) return;
                // process result here
                next();
            });
        }
    }
    next();
}

processArray(arr, processItem);

关于您的具体问题:

  

我不知道如果阵列太大会发生什么。是个   调用下一个函数时从内存释放的函数?要么   所有的功能都在内存中直到结束?

Javascript中的内存在任何正在运行的代码不再引用时以及垃圾回收器有时间运行时发布。由于您在此处运行一系列异步操作,因此垃圾收集器很可能在等待异步操作的http响应时定期运行,因此内存可能会被清除。函数只是Javascript中的另一种类型的对象,它们像其他任何东西一样被垃圾收集。当它们不再通过运行代码引用时,它们就有资格进行垃圾收集。

在您的特定代码中,因为您仅在异步回调中重新调用process_url(),所以没有堆栈构建(如在正常递归中)。 process_url()的先前实例已在调用异步回调之前完成,并且在调用process_url()的下一次迭代之前已完成。

通常,使用内置于当前版本的node.js中的promise并且是ES6 ECMAScript标准的一部分,可以更轻松地管理和协调多个异步操作。在当前版本的node.js中使用promises不需要外部库。

有关使用promises而不是使用promises对数组上的异步操作进行排序的许多不同技术的列表,请参阅:

How to synchronize a sequence of promises?

使用promises的第一步是“promisify”你的异步函数,以便它返回一个promise而不是回调。

function async_function_promise(url) {
    return new Promise(function(resolve, reject) {
        async_function(url, function(err, result) {
            if (err) {
                reject(err);
            } else {
                resolve(result);
            }
        });
    });
}

现在,您有一个返回promises的函数版本。

如果你希望你的异步操作一次一个地进行,那么下一个操作在前一个操作完成之前就不会开始,那么通常的设计模式是使用.reduce()这样:

function process_urls(array) {
    return array.reduce(function(p, url) {
        return p.then(function(priorResult) {
            return async_function_promise(url);
        });
    }, Promise.resolve());
}

然后,您可以这样称呼它:

var myArray = ["url1", "url2", ...];
process_urls(myArray).then(function(finalResult) {
    // all of them are done here
}, function(err) {
    // error here
});

还有一些Promise库具有一些有用的功能,使这种类型的编码更简单。我,我自己,使用Bluebird promise库。以下是使用Bluebird代码的外观:

var Promise = require('bluebird');
var async_function_promise = Promise.promisify(async_function);

function process_urls(array) {
    return Promise.map(array, async_function_promise, {concurrency: 1});
}

process_urls(myArray).then(function(allResults) {
    // all of them are done here and allResults is an array of the results
}, function(err) {
    // error here
});

注意,您可以将concurrency值更改为您想要的值。例如,如果将其增加到25之间的某个位置,您可能会获得更快的端到端性能(取决于服务器实现如何最佳地优化)。