【问题标题】:How to process a big array applying a async function for each element in nodejs?如何处理为nodejs中的每个元素应用异步函数的大数组?
【发布时间】:2015-12-10 00:42:54
【问题描述】:

我正在使用zombie.js 来抓取一个站点,我必须使用回调样式连接到每个url。关键是我有一个 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),这是我想解决这个问题的方法,我不知道如果数组太大会发生什么。调用下一个函数时,函数是否从内存中释放?还是所有函数都在内存中直到结束?

有什么想法吗?

【问题讨论】:

    标签: node.js zombiejs


    【解决方案1】:

    你的方案会奏效。我称之为“手动排序异步操作”。

    您正在执行的通用版本如下所示:

    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 并且是 ES6 ECMAScript 标准的一部分的 Promise,管理和协调多个异步操作要容易得多。在当前版本的 node.js 中使用 Promise 不需要外部库。

    有关使用 Promise 和不使用 Promise 对数组进行异步操作排序的多种不同技术的列表,请参阅:

    How to synchronize a sequence of promises?.

    使用 Promise 的第一步是“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);
                }
            });
        });
    }
    

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

    如果您希望一次执行一个异步操作,以便在前一个操作完成之前不会开始下一个操作,那么通常的设计模式是使用 .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 之间的某个值,您可能会获得更快的端到端性能(取决于服务器实现的最佳优化方式)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-15
      • 2020-09-02
      • 2018-02-26
      • 2020-09-12
      • 1970-01-01
      • 1970-01-01
      • 2019-04-17
      • 1970-01-01
      相关资源
      最近更新 更多