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

时间:2016-04-08 21:50:53

标签: javascript node.js asynchronous

我想向API-s发出http请求,以便为每个用户收集它的数据并插入到mongodb中。

我遇到的问题是,它正在立即执行所有请求,并且似乎卡在某处,我不知道发生了什么。

我是否正在使用异步库并在每次迭代中添加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);

我是JavaScript的新手,尤其是在处理异步时。

任何帮助?

2 个答案:

答案 0 :(得分:1)

在您的示例中,您错过了each异步方法iteratee(item, callback)中的iteratee参数。请看这个示例here

每次在iteratee函数中都需要调用callback来告诉异步继续进行处理。

each(collection, iteratee, [callback])

  •   

    collection - 要迭代的集合。

  •   

    iteratee(item,callback) - 应用于coll中每个项目的函数。迭代器传递一个回调(err),一旦完成就必须调用它。如果没有发生错误,则应该在没有参数或显式null参数的情况下运行回调。数组索引不会传递给iteratee。如果需要索引,请使用forEachOf。

  •   

    callback(err) - 当所有iteratee函数完成或发生错误时调用的可选回调。

如果你需要同步行为,没有probs!除了每个集合项将同步迭代之外,还有eachSeries方法具有相同的签名。

<强>更新

应实施变更:

通过async回调:

request(options, getTimeEntriesFromWorksnap(callback));

返回request回调函数所需的内容:

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

仅在将记录保存在数据库中后调用callback

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

重构嵌套循环(不确定timeEntriestimeEntry是什么,因此使用适当的异步方法来迭代这些数据结构):

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

答案 1 :(得分:1)

使用Async.eachLimit()向api发出批量请求...尝试使用此iterateThruAllStudents()函数。

我在here

之前已经有过相同的问题

请参阅限制here的教程。 虽然我将限制设为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);