2015-08-13 2 views
3

Я пытаюсь получить несколько документов из MongoDB и отправить все данные в массиве, но у меня возникли серьезные проблемы с пониманием того, как это можно сделать с помощью Node.js.Сложность асинхронного кодирования Node.js

Проблема в том, что в момент выполнения dataArray.push(tempObject)tempObject["data"] = tempDataArray еще не выполнен.

Мой код выглядит следующим образом:

app.post('/api/charts', function(req, res) { 
    var names = req.body.names; 
    var categories = req.body.categories; 

    var dataArray = []; 

    for (i = 0; i < names.length; i++) { 
    var tempObject = {}; 
    tempObject["name"] = names[i]; 
    Company.find({ name : names[i] }, function(err, result) { 
     if (err) { 
     throw err; 
     } 

     var tempDataArray = []; 

     for (k = 0; k < categories.length; k++) { 
     var tempDataObject = {}; 
     tempDataObject["name"] = categories[k]; 
     tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"]; 
     tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"]; 

     tempDataArray.push(tempDataObject); 

     } 
     tempObject["data"] = tempDataArray; 
    }); 

    dataArray.push(tempObject); 
    } 

    res.send(dataArray); 
}); 

Любые предложения о том, как правильно достичь желаемого результата будут оценены.

+0

Вы можете использовать объекты Promise в Nodejs? – Savaratkar

+0

У меня были схожие трудности с пониманием того, как это работает. Взгляните на [это] (http://stackoverflow.com/questions/15852043/mongoose-find). Короче говоря, вам нужно опубликовать все процессы, когда у вас есть данные - то есть в вашем обратном вызове при поиске, как в упаковке, так и при отправке ответа, как сказал @kyrylkov. – Pio

ответ

0

Используйте эту библиотеку

https://github.com/caolan/async 

И Используя этот код, ваш код будет выглядеть следующим образом:

var async = require("async"); 
app.post('/api/charts', function(req, res) { 
    var names = req.body.names; 
    var categories = req.body.categories; 

    var dataArray = []; 

    async.forEach(names, function(name, callback){ 

     var tempObject = {}; 
     tempObject["name"] = name; 
     Company.find({ name : name }, function(err, result) { 
      if (err) { 
       callback(err); 
      } else { 
       var tempDataArray = []; 

       for (k = 0; k < categories.length; k++) { 
        var tempDataObject = {}; 
        tempDataObject["name"] = categories[k]; 
        tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"]; 
        tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"]; 

        tempDataArray.push(tempDataObject); 

       } 
       tempObject["data"] = tempDataArray; 
       dataArray.push(tempObject); 
       callback(); 
      } 
     }); 
    }, function(err){ 
     if(err){ 
      res.send(err); 
     } else { 
      res.send(dataArray); 
     } 
    }); 

}); 
+0

Является ли это хорошим решением для производства? – martin

+0

Да. В этом случае forEach все имена будут выполняться параллельно и как только все будет выполнено, вы получите последнюю функцию обратного вызова (err), в которой мы ответили res.send. Если вы не хотите выполнять параллельную работу или хотите сохранить порядок результатов на основе массива имен, вы можете использовать forEachLimit –

0

Метод Company.find() принимает функцию обратного вызова, как это второй параметр. Этот обратный вызов вызывается после того, как данные компании извлекаются из базы данных. Это означает, что он может быть где угодно между несколькими миллисекундами и несколькими зашифрованными миллисекундами, пока он не будет вызван после вызова метода Company.find(). Но код сразу после Company.find() не будет отложен; он будет вызван сразу. Поэтому задержка обратного вызова заключается в том, почему dataArray.push(tempObject) всегда вызывается до tempObject["data"] = tempDataArray.

Кроме того, внешний цикл будет работать синхронно, и на каждой итерации будет выполнен отдельный вызов БД. Это не идеально, поэтому мы хотим получить это для цикла в обратном вызове. Таким образом, мы можем сделать что-то вроде:

app.post('/api/charts', function(req, res) { 
    var names = req.body.names; 
    var categories = req.body.categories; 

    // we just do one DB query where all the data we need is returned 
    Company.find({ name : names }, function(err, result) { 
     if (err) { 
      throw err; 
     } 

     var dataArray = []; 

     // we iteratre through each result in the callback, not outside it since 
     // that would cause blocking due to synchronous operation 
     for (i = 0; i < result.length; i++) { 
      var tempObject = {}; 
      tempObject["name"] = result[i].name; 

      var tempDataArray = []; 

      for (k = 0; k < categories.length; k++) { 
       var tempDataObject = {}; 
       tempDataObject["name"] = categories[k]; 
       tempDataObject["numbers"] = result[i]["data"][categories[k]]["numbers"]; 
       tempDataObject["dates"] = result[i]["data"][categories[k]]["dates"]; 

       tempDataArray.push(tempDataObject);  
      } 

      tempObject["data"] = tempDataArray; 
      dataArray.push(tempObject); 
     } 
     res.send(dataArray); 
    }); 
}); 

Есть много подходов к абстрактным Узлам событийной природы, такие как Promises (которые могут быть доступны либо в ECMA Script 6 или библиотека Promise, такие как Bluebird, Async и т.д.) , Но выше это базовый подход обратного вызова, который обычно используется в подобных приложениях Express.

0

Просто изменить:

tempObject["data"] = tempDataArray; 
}); 
dataArray.push(tempObject); 

To:

tempObject["data"] = tempDataArray; 
dataArray.push(tempObject); 
}); 
+0

. Это просто решит проблему. 'res.send (dataArray)' будет вызываться до того, как большинство, если не все, будут выполнены обратные вызовы. И это не касается синхронного подхода, который используется. –

Смежные вопросы