2016-04-09 15 views
-1

Bir dizi öğrenci aracılığıyla yinelemek ve her biri için http araması yapmak ve yanıtı ayrıştırmak ve mongodb'e eklemek istiyorum, bu yüzden bunu yapmak istiyorum Her öğrenciye birer birer birer birer eklenecek ve bir sonraki ile devam edecek, böylece CPU ve RAM Bellek için daha iyi olacak ...node.js'de promises kullanarak senkronize http çağrılarının nasıl yapılacağı

Şimdiye kadar bunu yapıyorum, ama bu bir sebepten dolayı Ben

var startDate = new Date("February 20, 2016 00:00:00"); //Start from February 
var from = new Date(startDate).getTime()/1000; 
startDate.setDate(startDate.getDate() + 30); 
var to = new Date(startDate).getTime()/1000; 

iterateThruAllStudents(from, to); 

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

      async.eachSeries(students, function iteratee(student, callback) { 
       if (student.worksnap.user != null) { 
        var worksnapOptions = { 
         hostname: 'worksnaps.com', 
         path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to, 
         headers: { 
          'Authorization': 'Basic xxx=' 
         }, 
         method: 'GET' 
        }; 

        promisedRequest(worksnapOptions) 
         .then(function (response) { //callback invoked on deferred.resolve 
          parser.parseString(response, 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); 
            }); 
           }); 
           callback(null); 
          }); 
         }, function (newsError) { //callback invoked on deferred.reject 
          console.log(newsError); 
         }); 
       } 
      }); 
     }); 
} 

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(Math.random()); 
       } 
      }); 

     }); 
} 

function promisedRequest(requestOptions) { 
    //create a deferred object from Q 
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 
    var deferred = Q.defer(); 
    var req = http.request(requestOptions, function (response) { 
     //set the response encoding to parse json string 
     response.setEncoding('utf8'); 
     var responseData = ''; 
     //append data to responseData variable on the 'data' event emission 
     response.on('data', function (data) { 
      responseData += data; 
     }); 
     //listen to the 'end' event 
     response.on('end', function() { 
      //resolve the deferred object with the response 
      console.log('http call finished'); 
      deferred.resolve(responseData); 
     }); 
    }); 

    //listen to the 'error' event 
    req.on('error', function (err) { 
     //if an error occurs reject the deferred 
     deferred.reject(err); 
    }); 
    req.end(); 
    //we are returning a promise object 
    //if we returned the deferred object 
    //deferred object reject and resolve could potentially be modified 
    //violating the expected behavior of this function 
    return deferred.promise; 
} 

It da .sonra içine saveEntry() seferde tüm öğrenciler için çağrıldığı görünüyor ... istedik ve sorunlu görünüyor. o sözlere, geri aramaları söz konusu olduğunda özellikle ... Herkes böyle bir şey elde herhangi fikri vardır

JavaScript yeni am ... Ben bu sorunu ve çözülmüş vermedi Tamam

+0

'parser.parseString()' nedir? Bu senkron veya asenkron çalışma mı? – jfriend00

+1

Ayrıca, bilmeniz gereken bir '' '' 'nı '' bir' '' '' '' '' '' '' '' '. Async operasyonunun bağırsaklarına geri dönüyor ve hiçbir zaman onu hiçbir yerde yakalayamıyorsunuz. Bu durumda atma kullanmayın. – jfriend00

+0

Ayrıca, neden'_.forEach (timeEntry) '? Bu noktada timeEntry nedir? – jfriend00

cevap

0

Öncelikle, birden iç içe işlemleri kod ve tüm zaman uyumsuz işlemler için vaat kullanırsanız güvenilir hataları işlemek için bir sürü daha kolay olacak. Bu, veritabanınızda yerleşik olarak vaat edilen vaatlerin nasıl kullanılacağını öğrenmek (mongoose kullandığınızı varsayar) ve daha sonra vaatleri kullanmak için diğer tüm async işlemlerini sarmalamayı öğrenmek anlamına gelir. İşte çitanýn ile sözlerini kullanarak bir çift bağlantılar şunlardır:

Plugging in your own promise library into Mongoose

Switching to use promises in Mongoose

Yani, Gelincik belgelerine bakarak, sen .exec() ve .save() zaten bu yüzden doğrudan kullanabilirsiniz sözlerini iade görebilirsiniz.

kod satırı ekleme (yani göstermek söz kütüphanesi beri) Q vaat kullanmak Mongoose söyleyecektir:

// tell mongoose to use Q promises 
mongoose.Promise = require('q').Promise; 

Ardından, gibi sözler kullanmayan bazı işlemleri promisify gerekiyor ayrıştırma adımı:

function parse(r) { 
    var deferred = Q.defer(); 
    parser.parseString(r, function(err, results) { 
     if (err) { 
      deferred.reject(err); 
     } else { 
      deferred.resolve(results); 
     } 
    }); 
    return deferred.promise; 
} 
saveTimeEntry() kolayca sadece veritabanında zaten söz desteğini kullanarak, bir söz dönmek için yazılabilir

:

function saveTimeEntry(item) { 
    return Student.findOne({'worksnap.user.user_id': item.user_id[0]}).populate('user').exec().then(function(student) { 
     student.timeEntries.push(item); 
     return student.save(); 
    }); 
} 

Şimdi, ana mantığı sözleri kullanarak yeniden yazmak için doğru parçalara sahipsiniz. Burada hatırlanması gereken en önemli şey, bir .then() işleyicisinden bir söz verirseniz, ana sözü vaat eden zincirlerdir. Bunu işleminizde çok kullanacağız.

: Bu yerlerde bir çift bu yapıyı kullanır
return array.reduce(function(p, item) { 
    return p.then(function() { 
     return someAsyncPromiseOperation(item); 
    }); 
}, Q()); 

söz kullanılarak çekirdek mantığı yeniden için: Bundan başka, bir dizi yineleme sekanslayıcı vaat için ortak bir tasarım modeli aşağıdaki gibi array.reduce() kullanmaktır
// tell mongoose to use Q promises 
mongoose.Promise = require('q').Promise; 

iterateThruAllStudents(from, to).then(function() { 
    // done successfully here 
}, function(err) { 
    // error occurred here 
}); 

function iterateThruAllStudents(from, to, callback) { 
    return Student.find({status: 'student'}).populate('user').exec().then(function (students) { 
     // iterate through the students array sequentially 
     students.reduce(function(p, student) { 
      return p.then(function() { 
       if (student.worksnap.user != null) { 
        var worksnapOptions = { 
         hostname: 'worksnaps.com', 
         path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + 
           '&from_timestamp=' + from + '&to_timestamp=' + to, 
         headers: {'Authorization': 'Basic xxx='}, 
         method: 'GET' 
        }; 

        return promisedRequest(worksnapOptions).then(function(response) { 
         return parse(response).then(function(results) { 
          // assuming results.time_entries is an array 
          return results.time_entries.reduce(function(p, item) { 
           return p.then(function() { 
            return saveTimeEntry(item); 
           }); 
          }, Q()); 
         }); 
        }); 
       } 
      }); 
     }, Q()) 
    }); 
} 

function parse(r) { 
    var deferred = Q.defer(); 
    parser.parseString(r, function(err, results) { 
     if (err) { 
      deferred.reject(err); 
     } else { 
      deferred.resolve(results); 
     } 
    }); 
    return deferred.promise; 
} 

function saveTimeEntry(item) { 
    return Student.findOne({'worksnap.user.user_id': item.user_id[0]}).populate('user').exec().then(function(student) { 
     student.timeEntries.push(item); 
     return student.save(); 
    }); 
} 

function promisedRequest(requestOptions) { 
    //create a deferred object from Q 
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 
    var deferred = Q.defer(); 
    var req = http.request(requestOptions, function (response) { 
     //set the response encoding to parse json string 
     response.setEncoding('utf8'); 
     var responseData = ''; 
     //append data to responseData variable on the 'data' event emission 
     response.on('data', function (data) { 
      responseData += data; 
     }); 
     //listen to the 'end' event 
     response.on('end', function() { 
      //resolve the deferred object with the response 
      console.log('http call finished'); 
      deferred.resolve(responseData); 
     }); 
    }); 

    //listen to the 'error' event 
    req.on('error', function (err) { 
     //if an error occurs reject the deferred 
     deferred.reject(err); 
    }); 
    req.end(); 
    return deferred.promise; 
} 

Kodunuz bunu yapmazsa, bir hata oluştuğunda, hiçbir hata, iterateThruAllStudents()'dan geri gönderilen söze geri dönecek ve hiçbir hata gizlenmeyecektir.


Ve sonra bazı iç içe girinti azaltmak için o kadar temizlik ve yararlı bir fayda fonksiyonu ekleyerek, bu şuna benzer:

Tabii
// tell mongoose to use Q promises 
mongoose.Promise = require('q').Promise; 

// create utility function for promise sequencing through an array 
function sequence(array, iterator) { 
    return array.reduce(function(p, item) { 
     return p.then(function() { 
      return iterator(item); 
     }); 
    }, Q()); 
} 

iterateThruAllStudents(from, to).then(function() { 
    // done successfully here 
}, function(err) { 
    // error occurred here 
}); 

function iterateThruAllStudents(from, to, callback) { 
    return Student.find({status: 'student'}).populate('user').exec().then(function (students) { 
     // iterate through the students array sequentially 
     return sequence(students, function(item) { 
      if (student.worksnap.user != null) { 
       var worksnapOptions = { 
        hostname: 'worksnaps.com', 
        path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + 
          '&from_timestamp=' + from + '&to_timestamp=' + to, 
        headers: {'Authorization': 'Basic xxx='}, 
        method: 'GET' 
       }; 
       return promisedRequest(worksnapOptions).then(function(response) { 
        return parse(response); 
       }).then(function(results) { 
        // assuming results.time_entries is an array 
        return sequence(results.time_entries, saveTimeEntry); 
       }); 
      } 
     }); 
    }); 
} 

function parse(r) { 
    var deferred = Q.defer(); 
    parser.parseString(r, function(err, results) { 
     if (err) { 
      deferred.reject(err); 
     } else { 
      deferred.resolve(results); 
     } 
    }); 
    return deferred.promise; 
} 

function saveTimeEntry(item) { 
    return Student.findOne({'worksnap.user.user_id': item.user_id[0]}).populate('user').exec().then(function(student) { 
     student.timeEntries.push(item); 
     return student.save(); 
    }); 
} 

function promisedRequest(requestOptions) { 
    //create a deferred object from Q 
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 
    var deferred = Q.defer(); 
    var req = http.request(requestOptions, function (response) { 
     //set the response encoding to parse json string 
     response.setEncoding('utf8'); 
     var responseData = ''; 
     //append data to responseData variable on the 'data' event emission 
     response.on('data', function (data) { 
      responseData += data; 
     }); 
     //listen to the 'end' event 
     response.on('end', function() { 
      //resolve the deferred object with the response 
      console.log('http call finished'); 
      deferred.resolve(responseData); 
     }); 
    }); 

    //listen to the 'error' event 
    req.on('error', function (err) { 
     //if an error occurs reject the deferred 
     deferred.reject(err); 
    }); 
    req.end(); 
    //we are returning a promise object 
    //if we returned the deferred object 
    //deferred object reject and resolve could potentially be modified 
    //violating the expected behavior of this function 
    return deferred.promise; 
} 

, bu kodun bir sürü ve hiçbir var Bunu test etmek için bir yol ve daha önce Mongoose kodu yazmamıştım, bu yüzden burada çok iyi şeyler olabilir, ama umarım genel fikri görebilir ve yaptığım herhangi bir hata ile çalışabilirsiniz.

+0

Peki, bu gerçekten çok iyiydi o zaman ben yaptım ... Bu problemi gözetmek için harcadığınız çabanı ve zamanını gerçekten takdir ediyorum. Umarım benden başka herkese yardım ederdi ... Kodumu urs gibi değiştirdim ve işe yaramış gibi görünüyor hepsi iyi :) –

0

gelecekte birisi bu sorunu varsa işte geldiğim bir çözüm.

var startDate = new Date("February 20, 2016 00:00:00"); //Start from February 
var from = new Date(startDate).getTime()/1000; 
startDate.setDate(startDate.getDate() + 30); 
var to = new Date(startDate).getTime()/1000; 

iterateThruAllStudents(from, to); 

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

      var counter = 1; 
      async.eachSeries(students, function iteratee(student, callback) { 
       counter++; 
       if (student.worksnap.user != null) { 
        console.log(''); 
        console.log('--------------'); 
        console.log(student.worksnap.user.user_id); 
        var worksnapOptions = { 
         hostname: 'worksnaps.com', 
         path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to, 
         headers: { 
          'Authorization': 'Basic xxxxx' 
         }, 
         method: 'GET' 
        }; 

        promisedRequest(worksnapOptions) 
         .then(function (response) { //callback invoked on deferred.resolve 
          parser.parseString(response, function (err, results) { 
           var json_string = JSON.stringify(results.time_entries); 
           var timeEntries = JSON.parse(json_string); 
           var isEmpty = _.isEmpty(timeEntries); // true 
           if (isEmpty) { 
            callback(null); 
           } 
           saveTimeEntry(timeEntries).then(function (response) { 
            console.log('all timeEntries for one student finished....Student: ' + student.worksnap.user.user_id + ' Student Counter: ' + counter); 
            callback(null); 
           }); 
          }); 
         }, function (newsError) { //callback invoked on deferred.reject 
          console.log(newsError); 
         }); 
       } else { 
        callback(null); 
       } 
      }); 
     }); 
} 

function saveTimeEntry(timeEntries) { 
    var deferred = Q.defer(); 
    _.forEach(timeEntries, function (timeEntry) { 
     _.forEach(timeEntry, function (item) { 
      Student.findOne({ 
        'worksnap.user.user_id': item.user_id[0] 
       }) 
       .populate('user') 
       .exec(function (err, student) { 
        if (err) { 
         //throw err; 
         console.log(err); 
        } 
        student.timeEntries.push(item); 
        student.save(function (err) { 
         if (err) { 
          console.log(err); 
          deferred.reject(err); 
         } else { 
          //console.log(Math.random()); 
         } 
        }); 

       }); 
     }); 
     deferred.resolve('finished saving timeEntries for one student...'); 
    }); 

    return deferred.promise; 
} 

function promisedRequest(requestOptions) { 
    //create a deferred object from Q 
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 
    var deferred = Q.defer(); 
    var req = http.request(requestOptions, function (response) { 
     //set the response encoding to parse json string 
     response.setEncoding('utf8'); 
     var responseData = ''; 
     //append data to responseData variable on the 'data' event emission 
     response.on('data', function (data) { 
      responseData += data; 
     }); 
     //listen to the 'end' event 
     response.on('end', function() { 
      //resolve the deferred object with the response 
      console.log('http call finished'); 
      deferred.resolve(responseData); 
     }); 
    }); 

    //listen to the 'error' event 
    req.on('error', function (err) { 
     //if an error occurs reject the deferred 
     console.log('inside On error.'); 
     console.log(err); 
     deferred.reject(err); 
    }); 
    req.end(); 
    //we are returning a promise object 
    //if we returned the deferred object 
    //deferred object reject and resolve could potentially be modified 
    //violating the expected behavior of this function 
    return deferred.promise; 
} 
+0

Hataları doğru bir şekilde saptamak ve işlemek için birçok hata işleme eksik. Düzenli geri çağrıları ve sözleri karıştırmak neredeyse hiç iyi bir fikir değildir. Buradaki iyi bir tasarım, tüm asenkron işlemlerinizi vaatlere dönüştürmek ve daha sonra sözler kullanarak tüm async mantığını yazmak olacaktır. Bunu senin için yapacaktım, ama yorumlarda sorduğum sorulara cevap vermedin, böylece yapamam. – jfriend00

+0

@ jfriend00, Daha iyi bir çözümünüz varsa, lütfen en iyi cevap olarak kabul edebilmemiz için lütfen posta gönderin ... Bu sadece benim için çalıştığım kadar temiz bir çözüm değildi .... –

+0

Ben ayrıldım Size daha iyi bir cevap verebilmek için cevapları bilmem gereken soruları soran çok sayıda yorum. Denedim, ancak bir cevap oluşturmak için eksik bilgilerle karşılaştım. Neden sorularıma cevap vermediğini anlamıyorum. – jfriend00