如何处理for循环中的回调(Node.JS) [英] How to handle callbacks in a for loop(Node.JS)

查看:92
本文介绍了如何处理for循环中的回调(Node.JS)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图用NodeJS编写代码,从外部API抓取数据,然后使用Mongoose将其填充到MongoDB中.在这两者之间,我将检查该特定对象是否已经存在于Mongo中.下面是我的代码.

I am trying to write a code with NodeJS where I grab data from an external API and then populate them in MongoDB using Mongoose. In between that, I'll check to see if that particular already exists in Mongo or not. Below is my code.

router.route('/report') // the REST api address
  .post(function(req, res) // calling a POST 
  {
    console.log('calling report API');
    var object = "report/" + reportID; // related to the API
    var parameters = '&limit=100' // related to the API
    var url = link + object + apiKey + parameters; // related to the API

    var data = "";
    https.get(url, function callback(response)
    {
      response.setEncoding("utf8");
      response.on("data", function(chunk)
      {
        data += chunk.toString() + ""; 
      });

      response.on("end", function()
      {
        var jsonData = JSON.parse(data);
        var array = jsonData['results']; // data is return in array of objects. accessing only a particular array
        var length = array.length;
        console.log(length);

        for (var i = 0; i < length; i++) 
        {
          var report = new Report(array.pop()); // Report is the schema model defined. 
          console.log('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^');
          console.log(i);
          console.log('*****************************');
          console.log(report);
          console.log('*****************************');
          // console.log(report['id']);

          /*report.save(function(err)
          {
            if(err)
              res.send(err);
          });*/

          Report.find({id:report['id']}).count(function(err, count) // checks if the id of that specific data already exists in Mongo
          {
            console.log(count);
            console.log('*****************************');
            if (count == 0) // if the count = 0, meaning not exist, then only save
            {
              report.save(function(err)
              {
                console.log('saved');
                if(err)
                  res.send(err);
              });
            }
          });
        };
        res.json({
                    message: 'Grabbed Report'
                  }); 
      });
      response.on("error", console.error);
    });
  })

我的问题是,由于NodeJS回调是并行的,因此不会按顺序调用它.我的最终结果将是这样的:

My problem is that since NodeJS callbacks are parallel, it is not getting called sequentially. My end result would be something like this :

  1. 呼叫报告API
  2. console.log(长度)= 100
  3. ^^^^^^^^^^^^^^^^^^^^^^^^^
  4. console.log(i)=以0开头
  5. *******************************
  6. console.log(report)=将存储在Mongo内部的数据
  7. *******************************
  8. 数字3-7重复100次,因为长度等于100
  9. console.log(count)= 0或1
  10. 数字9重复100次
  11. console.log('saved')
  12. 数字11重复100次
  13. 最后,仅100个数据中的最后一个存储在Mongo中
  1. Calling report API
  2. console.log(length) = 100
  3. ^^^^^^^^^^^^^^^^^^^^^^^^
  4. console.log(i) = starts with 0
  5. *******************************
  6. console.log(report) = the data which will be stored inside Mongo
  7. *******************************
  8. number 3 - 7 repeats 100 times as the length is equals to 100
  9. console.log(count) = either 0 or 1
  10. number 9 repeats 100 times
  11. console.log('saved')
  12. number 11 repeats 100 times
  13. Lastly, only the last out of 100 data is stored into Mongo

我需要的是某种处理这些回调的技术或方法,这些回调是一个接一个地执行,而不是在循环之后顺序执行.我可以肯定这是问题所在,因为我的其他REST API都可以正常工作.

What I need is some sort of technique or method to handle these callbacks which are executing one after the other and not sequentially following the loop. I am pretty sure this is the problem as my other REST APIs are all working.

我研究了异步方法,promise,递归函数以及其他一些我无法理解如何解决此问题的方法.我真的希望有人能对此事有所启发.

I have looked into async methods, promises, recursive functions and a couple others non which I could really understand how to solve this problem. I really hope someone can shed some light into this matter.

如果我在问问题的方式上有任何错误,也可以随时纠正我.这是我在StackOverflow上发布的第一个问题.

Feel free also to correct me if I did any mistakes in the way I'm asking the question. This is my first question posted in StackOverflow.

推荐答案

此问题称为. 还有许多其他方法,例如使用 Promise

This problem is termed as the "callback hell". There's lots of other approaches like using Promise and Async libraries you'll find.

对于原生 async ES7 将带来 您现在可以通过Transpiler库 Babel 真正开始使用它.

I'm more excited about the native async ES7 will bring, which you can actually start using today with transpiler library Babel.

但是到目前为止,我发现的最简单的方法是: 您可以取出较长的回调函数,并在外部定义它们.

But by far the simplest approach I've found is the following: You take out the long callback functions and define them outside.

router.route('/report') // the REST api address
    .post(calling_a_POST)

function calling_a_POST(req, res) {
    ...
    var data = "";
    https.get(url, function callback(response) {
        ...
        response.on("end", response_on_end_callback); // --> take out
        response.on("error", console.error);
    });
}

function response_on_end_callback() {                 // <-- define here
    ...
    for (var i = 0; i < length; i++) {
        var report = new Report(array.pop());
        ...
        Report.find({ id: report['id'] })
              .count(Report_find_count_callback);     // --> take out
    };
    res.json({
        message: 'Grabbed Report'
    });
}

function Report_find_count_callback(err, count) {     // <-- define here
    ...
    if (count == 0) {
        report.save(function(err) {                   // !! report is undefined here
            console.log('saved');
            if (err)
                res.send(err);                        // !! res is undefined here
        });
    }
}

一个警告是,您将无法访问过去用作回调的所有变量, 因为您已将它们排除在范围之外.

A caveat is that you won't be able to access all the variables inside what used to be the callback, because you've taken them out of the scope.

这可以通过依赖注入"包装来解决,以传递所需的变量.

This could be solved with a "dependency injection" wrapper of sorts to pass the required variables.

router.route('/report') // the REST api address
    .post(calling_a_POST)

function calling_a_POST(req, res) {
    ...
    var data = "";
    https.get(url, function callback(response) {
        ...
        response.on("end", function(err, data){       // take these arguments
            response_on_end(err, data, res);          // plus the needed variables
        });
        response.on("error", console.error);
    });
}

function response_on_end(err, data, res) {  // and pass them to function defined outside
    ...
    for (var i = 0; i < length; i++) {
        var report = new Report(array.pop());
        ...
        Report.find({ id: report['id'] })
            .count(function(err, count){
                Report_find_count(err, count, report, res);  // same here
            });
    };
    res.json({                                        // res is now available
        message: 'Grabbed Report'
    });
}

function Report_find_count(err, count, report, res) {        // same here
    ...
    if (count == 0) {
        report.save(function(err) {                   // report is now available
            console.log('saved');
            if (err)
                res.send(err);                        // res is now available
        });
    }
}


当我执行response_on_end函数时,出现了undefined:1 unexpected token u错误. 我非常确定这与以下行有关:var jsonData = JSON.parse(data) 我的response_on_end如下:var jsonData = JSON.parse(data); // problem here

When I execute the response_on_end function, I am getting the undefined:1 unexpected token u error. I am pretty much sure it has something to do with this line: var jsonData = JSON.parse(data) My response_on_end is as below: var jsonData = JSON.parse(data); // problem here

我意识到我在这里犯了一个错误:

I realize I made an error here:

function calling_a_POST(req, res) {
    ...
    var data = "";
    https.get(url, function callback(response) {
        ...
        //sponse.on("end", function(err, data){
        response.on("end", function(err){ // data shouldn't be here
            response_on_end(err, data, res);
        });
        response.on("error", console.error);
    });
}

我可以预见的另一个问题,实际上可能不会在这里出现,但是无论如何还是要讨论得更好. data变量,因为它是一个字符串,是与对象不同的原始类型,所以它被按值传递". 更多信息

Another problem I could forsee, which actually may not arise here but still would be better to talk about anyways. The data variable, since it's a string which is a primitive type unlike an object, it is "passed by value". More info

最好将变量包装在一个对象中并传递该对象,因为javascript中的对象总是通过引用传递".

It's better to wrap the variable in an object and pass the object, because objects in javascript are always "passed by reference".

function calling_a_POST(req, res) {
    ...
    // var data = ""; // 
    var data_wrapper = {};
    data_wrapper.data = {};                                // wrap it in an object
    https.get(url, function callback(response) {
        ...
        response.on("data", function(chunk){
            data_wrapper.data += chunk.toString() + "";   // use the dot notation to reference
        });
        response.on("end", function(err){ 
            response_on_end(err, data_wrapper, res);      // and pass that object
        });
        response.on("error", console.error);
    });
}

function response_on_end_callback(err, data_wrapper, res) {
    var data = data_wrapper.data;                         // later redefine the variable
    ...
    for (var i = 0; i < length; i++) {
        var report = new Report(array.pop());
        ...

这篇关于如何处理for循环中的回调(Node.JS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆