nodejs如何让for循环等到运行下一个循环实例

Posted

技术标签:

【中文标题】nodejs如何让for循环等到运行下一个循环实例【英文标题】:nodejs how to let for loop wait till running next instance of loop 【发布时间】:2017-05-30 12:49:20 【问题描述】:

我将 nodejs 与 mongodb 和 bigquery 一起使用。

所以因为似乎 bigquery 每个命令只允许 10k 插入。

所以我计算了主查询,并从 10k 循环到多少页。

计数查询得到 500k,因此 50 页或 500 个循环。

如何让循环等到循环的下一页运行?

代码:

var limit = 9999;

mongo_client.connect(mongo_url, function(err, db) 
    var query = '_id.date_visited':'$gte':'2016-01-01','$lt':'2016-02-01';

    db.collection('my_table').count(query,function(err, count)
        var pages = Math.ceil(count/limit);

        console.log("count: "+count);
        console.log("pages: "+pages);

        for(var page=0;page<pages;page++)
            var skip = page * limit;

            console.log("page: "+page);
            console.log("skip: "+skip);

            //HOW TO MAKE THIS loop wait till running next page of the loop
            db.collection('my_table').find(query).sort('_id.date_visited':1,'_id.hour_visited':1).limit(limit).limit(skip).toArray(function(err, db_results)  
                var documents = [];
                async.each(db_results, function (db_resultsx, cb) 
                    documents.push(db_resultsx);

                    if(documents.length == db_results.length)  
                        //console.log(documents);
                        bigqueryClient
                          .dataset(dataset)
                          .table('my_table')
                          .insert(documents)
                          .then((insertErrors) => 
                            console.log('Inserted');
                            //documents.forEach((row) => console.log(row));
                            console.error(insertErrors);
                            if (insertErrors && insertErrors.length > 0) 
                              console.log('Insert errors:');
                              insertErrors.forEach((err) => console.error(err));
                            
                          )
                          .catch((err) => 
                            console.error('ERROR:');
                            console.log(err);
                        );
                    
                );
            );
        
    );
);

【问题讨论】:

您已经在使用async.each。请参阅此行 async.each(db_results, function (db_resultsx, cb) cb 旨在在您想要发出循环的下一次迭代信号时执行。请小心,因为您在另一个异步方法之前有一个 if 语句,因此您应该在异步方法( bigQuery )的完成内以及需要配对的 else 条件中调用该“回调” if,否则我们不会发出继续循环的信号。 【参考方案1】:

我认为对于这种情况,for循环不是一个好的解决方案,您可以使用这样的递归调用进行循环:

function performQuery(queryIndex) 
    if( queryIndex >= limit ) return;

    db.exec('query', function(err, db_result) 
        // your code
        performQuery(queryIndex+1);
    )

performQuery(0);

【讨论】:

我喜欢这个想法,但是在代码内部,它会将数据上传到 bigquery,所以我希望它等到运行下一个 performQuery,或者我需要在 bigquery 回调上执行 performQuery?跨度> 如果您想等到 bigquery 结束,请在 bigquery 回调中运行 performQuery【参考方案2】:

我可能会用 async.eachSeries 替换 for 循环,这样你就可以决定循环的下一次迭代何时发生,因为 async.eachSeries 一次只会运行 1 个操作,你不会遇到同样的问题错误

编辑:

阅读完代码后,我认为async.timesSeries(我的评论更正,async.timesSeries 是正确的选项)是一个更好的选择。这是一个例子:

async.timesSeries(pages, function(page, next)

    var skip = page * limit;
    // ... the rest of your code here

    // when you want the next iteration to start, simply call:
    next();
    /*
    which will tell async that the current iteration is complete, 
    and it can do the next one. You can pass 2 parameters to next,
    the first parameter is an error, and if error is not null it will
    immediately call the function below, and the second parameter is an
    item you can pass that will be added to an object which will be sent
    as the second parameter in the function below
    */
,
function(err, coll)

    /*
    this function will get called if there's an error
    or when all iterations are completed
    */
);

上面的代码将替换你的 for 循环

【讨论】:

所以我需要将页数提供给 eachSeries 以便循环/迭代那么多次? 我稍微误读了这里的意图,在正确查看您的代码后async.times 可能对您更有用,因为它允许回调,因此您只在以下情况下执行循环的下一部分你想要它,它将运行n 次数 默认情况下在.times内它会等到里面的代码完成直到它进入下一个循环?还是我需要做其他事情?我是节点新手。 我已经更新了我的答案,并进行了更正,它应该是async.timesSeries 而不是async.times,因为async.times 将同时运行多个 天哪,我的大脑今天工作不正常。我觉得我一次做的事情太多了。更正了示例【参考方案3】:

如果您不想使用递归承诺,并且您事先知道项目的数量,您可以这样做:

// Create a "skip" array (there is certainly a nicer way to do it, with a modulo)
var skips = []; 
for(var page=0;page<pages;page++)
    skips.push(page * limit);


// Put your mongoDB read and write code in a function
// (that takes skip in entry and returns an array of documents)
var myMongoDBFunc = function (skip) 
    var documents = [];

    db.collection('my_table')
       .find(query)
       .limit(limit)
       .limit(skip)
       .toArray(function(err, db_results)  
           ...
       );
   ...
   return documents;


// And call it with async.concatSeries that will concatenate the results (documents)
async.concatSeries(skips, myMongoDbFunc, function(err, documents) 
    // Will be called at the end
);

如果您想优化并并行运行所有查询,只需将concatSeries 替换为concat(但不能保证顺序)。

如果你不关心返回的文件(显然你只是想写点东西),也许你可以使用async.seriesasync.parallel(自己检查,我不是特别知道async) .

【讨论】:

以上是关于nodejs如何让for循环等到运行下一个循环实例的主要内容,如果未能解决你的问题,请参考以下文章

如何等到承诺完成后再继续循环

等待每个 for 循环迭代完成,然后在 nodejs 中开始下一个

如何让 android 应用程序等到电话完成?

如何在我的 for 循环之外执行代码(需要等到循环完成从 Firebase 数据库中检索数据)?

如何在DispatcherTimer结束之前暂停for循环?

Cordova 在循环内检查 appAvailability 失败。如何让循环等到 appAvailability.check 执行?