简体   繁体   English

如何在nodejs中的async.each中进行同步http调用

[英]how to make synchronous http calls within async.each in nodejs

I want to make http requests to an API-s to collect for each user it's data and insert into mongodb. 我想向API-s发出http请求,以便为每个用户收集它的数据并插入到mongodb中。

The problem I am having is, it is doing all the requests at once, and seems it gets stuck somewhere and I don't know what is going on. 我遇到的问题是,它正在立即执行所有请求,并且似乎卡在某处,我不知道发生了什么。

Al thou I am using async library and add the request() method inside each iteration, and I dont know if this is the right way, here is the code: 你是我使用异步库并在每次迭代中添加request()方法,我不知道这是否正确,这里是代码:

  function iterateThruAllStudents(from, to) {
    Student.find({status: 'student'})
        .populate('user')
        .exec(function (err, students) {
            if (err) {
                throw err;
            }

            async.forEach(students, function iteratee(student, callback) {
                if (student.worksnap.user != null) {
                    var options = {
                        url: 'https://api.worksnaps.com/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to,
                        headers: {
                            'Authorization': 'Basic bGhNSVJkVUFwOE1DS2loOFVyZkFyOENEZEhPSXdCdUlHdElWMHo0czo='
                        }
                    };
                    request(options, getTimeEntriesFromWorksnap);
                }
                callback(); // tell async that the iterator has completed
            }, function (err) {
                console.log('iterating done');
            });
        });
}

    function getTimeEntriesFromWorksnap(error, response, body) {
        console.log(response.statusCode);
        if (!error && response.statusCode == 200) {
            parser.parseString(body, function (err, results) {
                var json_string = JSON.stringify(results.time_entries);
                var timeEntries = JSON.parse(json_string);
                _.forEach(timeEntries, function (timeEntry) {
                    _.forEach(timeEntry, function (item) {
                        saveTimeEntry(item);
                    });
                });
            });
        }
    }

    function saveTimeEntry(item) {
        Student.findOne({
                'worksnap.user.user_id': item.user_id[0]
            })
            .populate('user')
            .exec(function (err, student) {
                if (err) {
                    throw err;
                }
                student.timeEntries.push(item);
                student.save(function (err) {
                    if (err) {
                        console.log(err);
                    } else {
                        console.log('item inserted...');
                    }
                });

            });
    }

var from = new Date(startDate).getTime() / 1000;
startDate.setDate(startDate.getDate() + 30);
var to = new Date(startDate).getTime() / 1000;
iterateThruAllStudents(from, to);

I am new to JavaScript, especially when dealing with async. 我是JavaScript的新手,特别是在处理异步时。

Any help? 有帮助吗?

In your example you missed iteratee param in the each method of async - iteratee(item, callback) . 在你的例子中,你错过了async- iteratee(item, callback)each方法中的iteratee param。 Look at this example here . 看下面这个例子在这里

You need to call callback each time inside your iteratee function to tell async continue doing its processing. 每次在iteratee函数中都需要调用callback来告诉异步继续进行处理。

each(collection, iteratee, [callback])

  • collection - collection to iterate over. 集合 - 迭代的集合。

  • iteratee(item, callback) - function to apply to each item in coll. iteratee(item,callback) - 用于应用于coll中每个项目的函数。 The iteratee is passed a callback(err) which must be called once it has completed. 迭代器传递一个回调(err),一旦完成就必须调用它。 If no error has occurred, the callback should be run without arguments or with an explicit null argument. 如果没有发生错误,则应该在没有参数或显式null参数的情况下运行回调。 The array index is not passed to the iteratee. 数组索引不会传递给iteratee。 If you need the index, use forEachOf. 如果需要索引,请使用forEachOf。

  • callback(err) - Optional callback which is called when all iteratee functions have finished, or an error occurs. callback(err) - 当所有iteratee函数完成或发生错误时调用的可选回调。

If you need synchronous behavior, no probs! 如果你需要同步行为,没有probs! There is also eachSeries method with the same signature except every collection item will be iterated synchronously. 除了每个集合项将同步迭代之外,还有eachSeries方法具有相同的签名。

UPDATE: 更新:

Changes should be implemented: 应该实施变革:

Pass async callback: 传递async回调:

request(options, getTimeEntriesFromWorksnap(callback));

Return necessary for request callback function: 返回request回调函数所需的:

function getTimeEntriesFromWorksnap(callback) {
  return function(error, response, body) { 
    // ...       
    saveTimeEntry(item, callback);                       
    // ...
  }        
}

Call callback only after record is saved in database: 仅在将记录保存在数据库中后才调用callback

function saveTimeEntry(item, callback) {
  // ..
  student.save(callback);
  // ..
}

Refactor nested loops (not sure what timeEntries , timeEntry are, so use appropriate async method to iterate these data structures): 重构嵌套循环(不确定timeEntriestimeEntry是什么,所以使用适当的异步方法来迭代这些数据结构):

async.each(timeEntries, function (timeEntry, callback) {
   async.each(timeEntry, function (item, callback) {
       saveTimeEntry(item, callback);
   }, callback);
}, callback);

Use Async.eachLimit() to make batched request to the api...Try this iterateThruAllStudents() function. 使用Async.eachLimit()向api发出批量请求...尝试使用iterateThruAllStudents()函数。

I already had same question before here 我在之前已经有过同样的问题

See tutorial of limiting here . 见限制的教程在这里 Though i am making the limit as 5 but you can do whatever you want(10,50 etc). 虽然我限制为5但你可以做任何你想做的事(10,50等)。

function iterateThruAllStudents(from, to) {
  Student.find({status: 'student'})
    .populate('user')
    .exec(function (err, students) {
      if (err) {
        throw err;
      }
      async.eachLimit(students,5,function iteratee(student, callback) {
        if (student.worksnap.user != null) {
          var options = {
            url: 'https://api.worksnaps.com/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to,
            headers: {
              'Authorization': 'Basic bGhNSVJkVUFwOE1DS2loOFVyZkFyOENEZEhPSXdCdUlHdElWMHo0czo='
            }
          };
          request(options,getTimeEntriesFromWorksnap(callback));
        }
      }, function (err) {
        console.log(err);
        console.log('iterating done');
      });
    });
}

function getTimeEntriesFromWorksnap(cb) {
  return function(error, response, body){
    console.log(response.statusCode);
    if (!error && response.statusCode == 200) {
      parser.parseString(body, function (err, results) {
        var json_string = JSON.stringify(results.time_entries);
        var timeEntries = JSON.parse(json_string);
        async.each(timeEntries,function(timeEntry,cb1){
          async.each(timeEntry,function(item,cb2){
            saveTimeEntry(item,cb2);
          },function(err){
            if(err)
              cb1(err);
            else
              cb1();
          })
        },function(err){
          if(err)
            cb(err);
          else
            cb();
        });
        //_.forEach(timeEntries, function (timeEntry) {
        //  _.forEach(timeEntry, function (item) {
        //    saveTimeEntry(item);
        //  });
        //});
      });
    }
    cb(null);
  }
}

function saveTimeEntry(item,cb2) {
  Student.findOne({
      'worksnap.user.user_id': item.user_id[0]
    })
    .populate('user')
    .exec(function (err, student) {
      if (err) {
        return cb2(err);
      }
      student.timeEntries.push(item);
      student.save(function (err) {
        if (err) {
          console.log(err);
          //return cb2(err);//Do it if you wanna throw an error.
        } else {
          console.log('item inserted...');
        }
        cb2();
      });
    });
}

var from = new Date(startDate).getTime() / 1000;
startDate.setDate(startDate.getDate() + 30);
var to = new Date(startDate).getTime() / 1000;
iterateThruAllStudents(from, to);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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