【问题标题】:calling asynchronous functions only once completed in callback nodejs仅在回调nodejs中完成一次调用异步函数
【发布时间】:2016-07-28 07:18:56
【问题描述】:

我有以某种方式接收 100000 个数据集的代码。 然后有一个存储必须以一种只有在最后一次添加完成后才能开始下一个才能开始的方式访问。

以一种看起来像这样的同步方式..所以 add 方法会阻塞。

var data = [...]; //100000 datasets 
var syncstorage = require( 'syncstorage' ); // syncronous storage.

for( var i = 0 ; i < data.length() ; i++ ) {
    syncstorage.add( data[i] ); // will only return once stored
}

异步存储不会阻塞,但它会在回调完成时告诉您...

/////// async storage 

asyncstorage.add( data[i] , function(err) { /* can only execute another add once i get this response */ } )

我现在才想到这个:

var i = 0;
function execute() {
    if( i >= data.length()){
        return;
    }
    asyncstorage.add( data[i] , function(err) { i++; execute(); } )     
}

但是它会导致一个极端的调用堆栈

也许我需要一个事件发射器并在该回调中发出它? 有点像资源包装器? 这是如何解决的? ...遗憾的是,我没有发现与此特定问题有关的堆栈溢出结果;

排除解决方案

测试示例

var store = {add:function(d,cb){cb(null)}};
var d=[]; for(var i = 0 ; i < 100000; i ++) { d.push(i)}; d;
var async = require('async');
async.eachSeries(d,store.add);

不起作用! 这是因为 async 假定 iteratee 函数中将存在一个事件发射器。 因此,像上面这样的简单测试类在 Maximum call stack size exceeded

中运行

【问题讨论】:

    标签: node.js asynchronous software-design eventemitter


    【解决方案1】:

    使用承诺或Async

    var async = require('async');
    
    // Assuming that asyncstorage.add = function(chunk, callback) { ... }
    async.eachSeries(data, asyncstorage.add, function(err) { 
        if (err)
            console.log(err);
        ...
    });
    

    删除Maximum call stack size exceeded 的决定是调用nextTick。它是“让 node.js 有机会清除堆栈 (read more)”。

    改进的代码
    异步

    var async = require('async');
    
    var store = {
        add: function(chunk, cb){
            res.push(chunk);
            cb(null);
        }
    };
    var data = []; 
    for (var i = 0 ; i < 100000; i ++)  
        data.push(i);
    
    var res = []; // for test result
    
    async.eachSeries(data, 
        // on each iteration.
        function f(chunk, cb) {
            async.nextTick(function() {
                store.add(chunk, cb)
            });
        }, 
        // on done
        function(err) {
            console.log((err) ? err : ('done ' + res.length));
        }
    );
    

    事件发射器

    var data = []; 
    for (var i = 0; i < 100500; i++) 
        data.push(i);
    
    var store = {
        add: function (chunk, cb) { cb(null); }
    };
    
    var EventEmitter = require('events').EventEmitter;
    var e = new EventEmitter;
    
    e.on('next', function(i) {
        if (i > data.length) 
            return console.log(i, 'done'); 
    
        setImmediate(function() { // clear stack
            store.add(data[i], () => e.emit('next', i + 1))
        });
    })
    
    e.emit('next', 0);
    

    【讨论】:

    • "请注意,由于此函数将 iteratee 并行应用于每个项目,因此无法保证 iteratee 函数将按顺序完成。"也不能保证上一个函数在下一次调用之前已经完成。 caolan.github.io/async/docs.html#.each
    • series 可以工作,但非常不雅...... 1. 因为它是一个任务堆栈而不是数据堆栈(函数)...... 2. add 函数完成得很快,因为它是异步的,因为它将“在前一个功能完成后每个都运行”。也可以准并行执行。不等待异步添加完成回调..所以我必须用这些东西填充数组中的 100k 函数..如果你的意思不同于每个函数,请编辑你的答案
    • @Summer-Sky:async 非常高效,因此在性能方面,即使不是最佳的,它也会比您当前的解决方案更好。
    • @Summer-Sky:“这是一个任务堆栈,而不是数据堆栈”是什么意思?对于第 2 点,它将等待来自迭代器的回调完成,这意味着它将等待 asyncstorage.add 回调,这意味着添加已完成并在第二个开始之前完成。
    • 再次查看我的答案。
    猜你喜欢
    • 1970-01-01
    • 2017-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多