2015-12-11 3 views
1

im пытается подсчитать, сколько людей из каждого пола есть в списке json, переданном клиентом с запросом POST (на сервере Node.js). У меня проблемы с асинхронизацией javascript, обратными вызовами и закрытием.Выполнить цикл запроса POST на сервере Node.js

Что я хочу: получить список от клиента, для каждой записи спросить мою коллекцию, если это утро, аф или аи, сосчитать, сколько фс, мс, и нам есть, отправить массив клиент с тремя значениями.

Я всегда получаю «Заголовки заданных наборов после их отправки» или подобные ошибки из-за выполнения асинхронного вызова. Я пробовал разные обратные вызовы и много разных вариантов.

Это как функционирует на сервере выглядит следующим образом:

app.post('/genderize', function(req, res){ 
     createCounter("conto", req, function(req,contat){ 
      count(req, contat); 
     }).then(res.send(result)); 
    }); 

function createCounter(nome, req, callback) { 
     result = [0,0,0]; 
     var contatore = function(){ 
      var m = 0; 
      var f = 0; 
      var u = 0; 

      addM = function(){ console.log("m++ "+result[1]);result[1]++; }; 
      addF = function(){ f++; }; 
      addU = function(){ u++; }; 

      getM = function(){ return this.m;}; 

      getResult = function(){ 
       console.log(result+ " * "+ getM() + " * " + this.u + " * "+ this.f); 
       return result; 
      }; 
      return { 
       addM: addM, 
       addF: addF, 
       addU: addU, 
       getResult: getResult 
       }; 
     } 
      callback(req, contatore()); 
    } 

function count(req, counter){ 
     var collection = db.get('nomi'); 
     var data = req.body.data; 
     data.forEach(function(value, i){ 
      collection.find({ nome : req.body.data[i].name.split(" ")[0].toUpperCase() }, { fields: {_id:0, nome:0}}, function (err, docs) { 
         if (!isEmptyObject(docs)) { 
          docs = JSON.parse(JSON.stringify(docs));; 
          if(docs[0].sesso == "M"){     
           counter.addM(); 
          } else { 
           counter.addF(); 
          } 
         } else { 
          counter.addU(); 
         } 
      }); 
     }); 
    } 
+0

Прежде всего, createCounter не является обещанием, поэтому вы должны удалить его. – chapinkapa

+0

Можете ли вы вставить, как выглядит ваш JSON от клиента? – chapinkapa

+0

Скрипт итерации через JSON без проблем, это не так. Должен ли я сделать обещание? перед тем, как поставить «then», я вызвал res.send как обратный вызов, но он не подождал –

ответ

0

Есть несколько проблем с этим примером, но главное, что вы пропустили, что при выполнении вашего запроса к базе данных, то collection.find вызов будет немедленно вернуться, но только выполнить свою функцию обратного вызова (function(err, docs)) в некоторых позже после того, как база данных ответила.

Вот рабочий переписан:

app.post('/genderize', function(req, res) { 
    if (!req.body.data || req.body.data.length === undefined) { 
     return res.status(400).send('Invalid request body.'); 
    } 
    countGenders(db.get('nomi'), req.body.data, function (err, genders) { 
     if (err) return res.status(500).send('Unable to process request.'); 
     res.send([genders.M, genders.F, genders.U]); 
    }); 
}); 

function getGenderFromName(collection, name, next) { 
    collection.find({nome : name.split(" ")[0].toUpperCase()}, {fields: {_id:0, nome:0}}, function (err, docs) { 
     if (err) return next(err); 
     var gender = 'U'; 
     if (docs && docs.length > 0) { 
      gender = (docs[0].sesso == "M") ? 'M' : 'F'; 
     } 
     next(null, gender);  
    }); 
} 

function countGenders(collection, data, next) { 
    var result = { M: 0, F: 0, U: 0 }; 
    var series = function(i) { 
     if (i == data.length) return next(null, result); 
     getGenderFromName(collection, data[i].name, function(err, gender) { 
      if (err) return next(err); 
      result[gender]++; 
      series(i+1); 
     }); 
    }; 
    series(0); 
} 

Позволяет просмотреть изменения:

  • Убрана createCounter структуру. Для этого простого примера не требуется тяжелый шаблон get/set.
  • Проверен для значений ошибок в каждом асинхронном обратном вызове

    if (err) return next(err); 
    

    внутри обработчика маршрута, как правило, вы хотите, чтобы завершить запрос с res.status(500).send(). В большинстве других случаев return next(err) будет «пузырить» ошибку.

  • Перемещенный запрос базы данных в новую функцию, getGenderFromName. В основном он сохраняет исходный код. Это необязательно, но существенно улучшает читаемость функции count.
  • Наконец, переписана функция count с использованием соответствующего асинхронного шаблона итерации, любезно предоставленного http://book.mixu.net/node/ch7.html. Mixu дает очень легкое для понимания объяснение асинхронного узла, дает ему чтение.

    Еще лучший вариант - использовать отличный модуль async. Вы можете переписать метод count как

    function countGenders(collection, data, next) { 
        var result = { M: 0, F: 0, U: 0 }; 
        async.eachSeries(
         data, 
         function (value, next) { 
          getGenderFromName(collection, value.name, function(err, gender) { 
           if (err) return next(err); 
           result[gender]++; 
           next(); 
          }); 
         }, 
         function (err) { next(err, results); } 
        ); 
    } 
    

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

+0

Да, это работает! Я проверю документацию модуля aync и раздел mixu. спасибо за вашу помощь –

+0

Рад помочь и удачи, узкая асинхронная система сложна, чтобы понять сначала, поэтому просто держись на ней, пока она не нажмет. – nicknystrom

0

Вот лучший способ сделать это. Это действительно очищает асинхронный характер javascript. Ознакомьтесь с асинхронной библиотекой, которую я использую здесь.

var collection = db.get('nomi'); 
var async = require('async'); 

app.post('/genderize', function(req, res){ 
    let countingObject = { 
     females: 0, 
     males: 0, 
     unknown: 0 
    }; 

    async.each(req.body.data, function(name, callback) { 
     collection.findOne({ nome : name.split(" ")[0].toUpperCase() }, { fields: {_id:0, nome:0}}, function (err, nameObject) { 
      //instead, maybe check if it is male, female, or otherwise mark as unknown? 
      if (!isEmptyObject(nameObject)) { 
       //this object probably has getters that you could use instead 
       nameObject = JSON.parse(JSON.stringify(nameObject)); 
       if(nameObject.sesso == "M"){     
        countingObject.males++; 
       } else { 
        countingObject.females++; 
       } 
      } else { 
       countingObject.unknown++; 
      } 
      callback(); 
     }); 
    }, function() { 
     res.setHeader('Content-Header', 'application/json'); 
     res.send(JSON.stringify(countingCallback)); 
    }); 
}); 
+0

Я забыл добавить «callback()», но добавил его. Обратите внимание, что он вызывает вызовы res.send только после того, как все имена итеративны, и после того, как их соответствующее «nome» найдено из базы данных. – chapinkapa

+0

Я думаю, что async.each - это то, что я искал! В любом случае это возвращает неожиданный идентификатор ошибки при подсчете строки объекта 5 вашего скрипта. Почему вы используете kwyword let? –

+0

Ах, вы, вероятно, не обновили узел или не используете 'use strict' ;. Вы можете использовать var вместо – chapinkapa

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