简体   繁体   中英

async.waterfall in a For Loop in Node.js

When using async.waterfall within a for loop, it appears that the for loop iterates before the nested async.waterfall finishes all its steps. How can this be avoided?

for(var i = 0; i < users.length; i++ ) {

    console.log(i)

    async.waterfall([
        function(callback) {
            callback(null, 'one', 'two');
        },
        function(arg1, arg2, callback) {
          // arg1 now equals 'one' and arg2 now equals 'two'
            callback(null, 'three');
        },
        function(arg1, callback) {
            // arg1 now equals 'three'
            callback(null, 'done');
        }
    ], function (err, result) {
        // result now equals 'done'
        console.log('done')
    });


}

Output

0
1
2
3
4
done
done
done
done
done

Desired Output

0
done
1
done
2
done
3
done
4
done

You can use like this with async's forEachLimit

var async = require("async")
var users = []; // Initialize user array or get it from DB

async.forEachLimit(users, 1, function(user, userCallback){

    async.waterfall([
        function(callback) {
            callback(null, 'one', 'two');
        },
        function(arg1, arg2, callback) {
            // arg1 now equals 'one' and arg2 now equals 'two'
            callback(null, 'three');
        },
        function(arg1, callback) {
            // arg1 now equals 'three'
            callback(null, 'done');
        }
    ], function (err, result) {
        // result now equals 'done'
        console.log('done')
        userCallback();
    });


}, function(err){
    console.log("User For Loop Completed");
});

You would need to apply a recursive pattern. My suggestion would be something like this

function foo(properties, callback){
/*Initialize inputs w/ useful defaults.  Using a properties object simply because it looks nicer, you can use individual values if you'd like.*/
        properties.start = properties.start || 0;
        properties.end = properties.end || 10; //or any default length
        properties.data = properties.data || [];

        async.waterfall([
          function(callback) {
        },
        function(arg1, arg2, callback) {
          /* arg1 now equals 'one' and arg2 now equals 'two' you can do something with that before continuing processing */
        },
        function(arg1, callback) {
            /* arg1 now equals 'three', you can do something with that before continuing processing */

        }
    ], function (err, result) {
        // result now equals 'done' 
        // now that we have fully processed one record, we need to either finish or recurse to the next element.  

     if(properties.index >= properties.end){
       callback();
     }
     else{
       properties.index++;
       foo(properties, callback);
     }

    })}

I've passed the callback into each of the function callbacks. You could chose to end the recursion early this way if you wanted to, or you could do anything elsein that spot that you wanted to. This is similar to a question that I asked recently: Patterns for asynchronous but sequential requests which has some other interesting solutions to the problem as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM