2012-05-24 5 views
8

कृपया नीचे दिए गए कोड स्निपेट को देखें। मेरे पास 'stuObjList' नामक JSON ऑब्जेक्ट्स की एक सरणी है। मैं विशिष्ट जेएसओएन ऑब्जेक्ट्स को एक निश्चित ध्वज सेट के साथ खोजने के लिए सरणी के माध्यम से लूप करना चाहता हूं, और फिर अधिक डेटा पुनर्प्राप्त करने के लिए एक डीबी कॉल करें।नोडजेज़ में, फॉर लूप को कैसे रोकें जब तक कि mongodb कॉल रिटर्न

संभोग, फोर लूप डीबी कॉल वापस लौटने की प्रतीक्षा नहीं करता है और j == लंबाई के अंत तक पहुंचता है। और जब डीबी कॉल रिटर्न, सूचकांक 'जे' सरणी सूचकांक से परे है। मैं समझता हूं कि कैसे node.js काम करता है और यह अपेक्षित व्यवहार है।

मेरा सवाल है, यहां पर काम क्या है। मैं जो हासिल करने की कोशिश कर रहा हूं उसे कैसे प्राप्त कर सकता हूं? धन्यवाद, --su

............... 
............... 
............... 
else 
{ 
    console.log("stuObjList.length: " + stuObjList.length); 
    var j = 0; 
    for(j = 0; j < stuObjList.length; j++) 
    { 
    if(stuObjList[j]['honor_student'] != null) 
    {  
     db.collection("students").findOne({'_id' : stuObjList[j]['_id'];}, function(err, origStuObj) 
     { 
     var marker = stuObjList[j]['_id']; 
     var major = stuObjList[j]['major']; 
     }); 
    } 

    if(j == stuObjList.length) 
    { 
     process.nextTick(function() 
     { 
     callback(stuObjList); 
     }); 
    } 
    } 
} 
}); 

उत्तर

8

"async" अतुल्यकालिक पाशन दूर सार संक्षेप और अपने कोड को पढ़ने/बनाए रखने के लिए करने के लिए एक बहुत ही लोकप्रिय मॉड्यूल है उपयोग कर सकते हैं। उदाहरण के लिए:

var async = require('async'); 

function getHonorStudentsFrom(stuObjList, callback) { 

    var honorStudents = []; 

    // The 'async.forEach()' function will call 'iteratorFcn' for each element in 
    // stuObjList, passing a student object as the first param and a callback 
    // function as the second param. Run the callback to indicate that you're 
    // done working with the current student object. Anything you pass to done() 
    // is interpreted as an error. In that scenario, the iterating will stop and 
    // the error will be passed to the 'doneIteratingFcn' function defined below. 
    var iteratorFcn = function(stuObj, done) { 

     // If the current student object doesn't have the 'honor_student' property 
     // then move on to the next iteration. 
     if(!stuObj.honor_student) { 
      done(); 
      return; // The return statement ensures that no further code in this 
        // function is executed after the call to done(). This allows 
        // us to avoid writing an 'else' block. 
     } 

     db.collection("students").findOne({'_id' : stuObj._id}, function(err, honorStudent) 
     { 
      if(err) { 
       done(err); 
       return; 
      } 

      honorStudents.push(honorStudent); 
      done(); 
      return; 
     }); 
    }; 

    var doneIteratingFcn = function(err) { 
     // In your 'callback' implementation, check to see if err is null/undefined 
     // to know if something went wrong. 
     callback(err, honorStudents); 
    }; 

    // iteratorFcn will be called for each element in stuObjList. 
    async.forEach(stuObjList, iteratorFcn, doneIteratingFcn); 
} 

तो तुम इस तरह इसका इस्तेमाल कर सकते हैं:

getHonorStudentsFrom(studentObjs, function(err, honorStudents) { 
    if(err) { 
     // Handle the error 
     return; 
    } 

    // Do something with honroStudents 
}); 

ध्यान दें कि .forEach() "समानांतर में" stuObjList में प्रत्येक तत्व के लिए अपने इटरेटर समारोह कॉल करेंगे (यानी, यह इंतजार नहीं होगा एक पुनरावर्तक फ़ंक्शन के लिए इसे अगले सरणी तत्व पर कॉल करने से पहले एक सरणी तत्व के लिए बुलाया जा रहा है)। इसका अर्थ यह है कि आप वास्तव में ऑर्डर करने वाले ऑर्डर की भविष्यवाणी नहीं कर सकते हैं - या अधिक महत्वपूर्ण बात यह है कि डेटाबेस कॉल - चलाएगा। अंतिम परिणाम: सम्मान छात्रों के अप्रत्याशित आदेश। यदि आदेश महत्वपूर्ण है, तो .forEachSeries() फ़ंक्शन का उपयोग करें।

+0

@Clint ... बहुत धन्यवाद। मैं इसे आज़माउंगा और आपको बताऊंगा कि यह कैसे काम करता है। –

+0

@Clint ... जो काम किया। बहुत धन्यवाद! –

1

आह अतुल्यकालिक सोचने की सुंदरता और निराशा। इस प्रयास करें:

............... 
............... 
............... 
else 
{ 
    console.log("stuObjList.length: " + stuObjList.length); 
    var j = 0, found = false, step; 
    for(j = 0; j < stuObjList.length; j++) 
    { 
    if(stuObjList[j]['honor_student'] != null) 
    {  
     found = true; 
     step = j; 
     db.collection("students").findOne({'_id' : stuObjList[j]['_id'];}, function(err, origStuObj) 
     { 
     var marker = stuObjList[step]['_id']; // because j's loop has moved on 
     var major = stuObjList[step]['major']; 
     process.nextTick(function() 
     { 
      callback(stuObjList); 
     }); 
     }); 
    } 

    } 
    if (!found) { 
    process.nextTick(function() 
    { 
     callback(stuObjList); 
    }); 
    } 
} 
}); 

आप अपने "जब मेरा कार्य पूर्ण हो" चरणों जटिल हो रही हैं, उन्हें एक और कार्य करने के लिए निकालने, और बस प्रत्येक स्थान से इसे कहते हैं। इस मामले में यह केवल 2 लाइनें थी, यह डुप्लिकेट करने के लिए उचित लग रहा था।

+0

@robrich ... आपके उत्तर के लिए धन्यवाद। लेकिन मुझे लगता है, यहां कुछ भ्रम है। मैं वापस लौटना नहीं चाहता जब तक कि मैं stuObjList में सभी वस्तुओं के माध्यम से पुनरावृत्त नहीं किया है। आपके कोड से ऐसा लगता है, जैसे ही मैं पहली वस्तु को पूरा करता हूं जो आईएफ स्थिति से मेल खाता है, फ़ंक्शन वापस आ जाएगा। क्या ऐसा करने में कोई है? –

+0

फिर आप सही हैं, यह समाधान इतना अच्छा काम नहीं करेगा, और हमें jQuery के डिफरर्ड को देखने की आवश्यकता होगी। – robrich

1

आवश्यकता को देखते हुए, आप भी अंडरस्कोर के "फिल्टर" विधि http://documentcloud.github.com/underscore/#filter

var honor_students = _.filter(stuObjList, function(stud) { return stu['honor_student'] != null }); 
if (honor_students.length === 0) { 
    process.nextTick(function() { callback(stuObjList); }); 
} else { 
    var honor_students_with_more_data = []; 
    for (var i = 0; i < honor_students.length; i++) { 
    db.collection("students").findOne({'_id' : honor_students[i]['_id'];}, function(err, origStuObj) { 
     // do something with retrieved data 
     honor_students_with_more_data.push(student_with_more_data); 
     if (honor_students_with_more_data.length === honor_students.length) { 
     process.nextTick(function() { callback(stuObjList); }); 
     } 
    } 
    } 
} 
0
And when the db call returns, the index 'j' is beyond the array index. 

ऐसा लगता है कि आपको प्रत्येक लूप पुनरावृत्ति पर जे की "प्रतिलिपि" लेने की आवश्यकता है। आप इसे बंद करने के साथ बना सकते हैं।

if(stuObjList[j]['honor_student'] != null) 
{ 

    (function(j_copy){ 
     db.collection("students").findOne({'_id' : stuObjList[j_copy]['_id'];}, function(err, origStuObj) 
     { 
      var marker = stuObjList[j_copy]['_id']; 
      var major = stuObjList[j_copy]['major']; 
     }); 
    })(j) 

} 

इस तरह आप प्रत्येक पुनरावृत्ति पर जे की स्थिति को बचा रहे हैं। यह राज्य प्रत्येक आईआईएफई के अंदर सहेजा जाता है। आपके पास कई बचाए गए राज्य होंगे - लूप के लिए। जब डीबी रिटर्न:

var marker = stuObjList[j_copy]['_id']; 

j_copy मूल j है, जो यह

if(stuObjList[j]['honor_student'] != null) 

मैं जानता हूँ कि मेरी समझा कौशल बहुत बुरा कर रहे हैं के क्षण में है का मान रखेंगे, लेकिन मैं आशा है कि आप कर सकते हैं मेरे कहे का मतलब समझो।

संपादित करें: इस तरह हम तत्काल आवंटित फ़ंक्शन और उसके दायरे को जे की अलग निजी प्रति रखने के लिए उपयोग कर रहे हैं। प्रत्येक पुनरावृत्ति पर नया आईआईएफई अपने निजी दायरे के साथ बनाया गया है। इस दायरे में - प्रत्येक पुनरावृत्ति के लिए हम j_copy = j करते हैं।और यह j_copy प्रत्येक बार लूप द्वारा अधिलेखित किए बिना IIFE के अंदर उपयोग किया जा सकता है।

संबंधित मुद्दे