2012-05-14 26 views
14

मैं वर्तमान में नोडजे, एक्सप्रेसजे, मोंगोडीबी, एचटीएमएल 5, ... का उपयोग कर 3 दोस्तों के साथ एक परियोजना पर काम कर रहा हूं ... चूंकि हम इन प्रौद्योगिकियों के लिए बिल्कुल नए हैं, इसलिए हम कुछ समस्याओं में फंस गए हैं। एक बड़ी समस्या जिसे मैं समाधान नहीं ढूंढ पा रहा हूं वह कुछ कोड के असीमित निष्पादन है।नोडजेस, जावास्क्रिप्ट: .forEach एसिंक्रोनस लगता है? सिंक्रनाइज़ेशन की आवश्यकता है

मैं प्रत्येक लूप को समाप्त करने के लिए चाहता हूं, ताकि मेरे पास एक अद्यतन ऑनलाइन मित्र सूची हो, और res.render (जिसमें मैं ऑनलाइन मित्र सूची पास करता हूं) निष्पादित करने से पहले, क्योंकि वर्तमान में यह res.render पहले करता है यह लूप खत्म करता है। कोड:

function onlineFriends(req, res) { 
var onlinefriends = new Array(); 
onlinefriends.push("mark"); 
FriendList.findOne({ 
    owner: req.session.username 
}, function (err, friendlist) { 
    friendlist.friends.forEach(function (friend) { // here forEach starts 
     OnlineUser.findOne({ 
      userName: friend 
     }, function (err, onlineFriend) { 
      if (onlineFriend != null) { 
       onlinefriends.push(onlineFriend.userName); 
       console.log("a loop"); 
      } 
     }); 

    }); 
     console.log("online friends: " + onlinefriends); 
     console.log("redirecting"); 
     res.render('index', { // this is still inside the forEach function 
      friendlist: friendlist.friends, 
      onlinefriendlist: onlinefriends, 
      username: req.session.username 
     });// and here it ends 
}); 

}

उत्पादन इस प्रकार होगी:

online friends: mark 
redirecting 
a loop 
a loop 
a loop 
a loop 
a loop 
a loop 
a loop 

के रूप में यहाँ पर चर्चा की (JavaScript, Node.js: is Array.forEach asynchronous?), जवाब यह है कि के लिए-प्रत्येक ब्लॉक कर रहा है, लेकिन मेरे उदाहरण में है ऐसा लगता है कि यह गैर-अवरुद्ध हो रहा है क्योंकि यह लूपिंग समाप्त होने से पहले res.render निष्पादित करता है? मैं यह कैसे सुनिश्चित कर सकता हूं कि प्रत्येक के लिए समाप्त हो गया है, इसलिए मेरे पास एक अद्यतित ऑनलाइन मित्र सूची (और मित्रसूची) है जो मैं res.render के बजाय res.render के पास हो सकता है, क्योंकि पहले लूप खत्म हो जाता है (जो मुझे ऑनलाइन उपयोगकर्ताओं की गलत सूची देता है)?

बहुत बहुत धन्यवाद!

उत्तर

15

निम्नलिखित कंसोल लॉग:

console.log("a loop"); 

एक कॉलबैक

मुझे विश्वास है कि समारोह OnlineUser.findOne की कॉलबैक() एसिंक्रोनस रूप से कहा जाता है के अंदर है, यही वजह है कि कोड प्रवेश करेंगे रीडायरेक्ट लॉग

के बाद "लूप" सभी लूप कॉलबैक निष्पादित किए जाने के बाद आपको पुनर्निर्देशन करना चाहिए

कुछ की तरह:

var count = 0; 
friendlist.friends.forEach(function (friend) { // here forEach starts 
    OnlineUser.findOne({ 
     userName: friend 
    }, function (err, onlineFriend) { 
     count++; 
     if (onlineFriend != null) { 
      onlinefriends.push(onlineFriend.userName); 
      console.log("a loop"); 
     } 
     if(count == friendlist.friends.length) { // check if all callbacks have been called 
      redirect(); 
     } 
    }); 
}); 

function redirect() { 
    console.log("online friends: " + onlinefriends); 
    console.log("redirecting"); 
    res.render('index', { // this is still inside the forEach function 
     friendlist: friendlist.friends, 
     onlinefriendlist: onlinefriends, 
      username: req.session.username 
    });// and here it ends 
} 
+0

धन्यवाद !! यह काम करता है, लेकिन जावास्क्रिप्ट में इस प्रकार का प्रोग्रामिंग "खराब अभ्यास" के रूप में माना जाता है? या यह इस तरह से काम करने के लिए पूरी तरह से कानूनी है? – Jeroen

+1

यह एक बुरा अभ्यास नहीं है, यह जावास्क्रिप्ट कैसे काम करता है, आपको केवल कॉलबैक के लिए उपयोग करने की आवश्यकता है .. वैसे भी, यह स्पष्ट रूप से सबसे साफ तरीका नहीं है, आप अपना स्वयं का फ़ंक्शन खरीद सकते हैं, कार्यक्षमता को लपेट सकते हैं, या कुछ ऐसा उपयोग कर सकते हैं: https : //github.com/coolaj86/futures/tree/v2.0/forEachAsync, जो फंक्शन कॉलबैक के आदेश की गारंटी देता है (जो कोड मैंने प्रदान किया है) – BFil

+0

धन्यवाद, यह मेरे लिए पूरी तरह से काम करता है :) – thtsigma

1

यह ठीक से jsbeautifier इंडेंट के माध्यम से अपने कोड चल रहा है और क्यों ऐसा होता है आपको पता चलता है:

function onlineFriends(req, res) { 
    var onlinefriends = new Array(); 
    onlinefriends.push("mark"); 
    FriendList.findOne({ 
     owner: req.session.username 
    }, function (err, friendlist) { 
     friendlist.friends.forEach(function (friend) { // here forEach starts 
      console.log("vriend: " + friend); 
      OnlineUser.findOne({ 
       userName: friend 
      }, function (err, onlineFriend) { 
       if (onlineFriend != null) { 
        onlinefriends.push(onlineFriend.userName); 
        console.log("online friends: " + onlinefriends); 
       } 
      }); 
      console.log("nu door verwijzen"); 
      res.render('index', { // this is still inside the forEach function 
       friendlist: friendlist.friends, 
       onlinefriendlist: onlinefriends, 
       username: req.session.username 
      }); 
     }); // and here it ends 
    }); 

तो ... हमेशा अपने कोड को ठीक से इंडेंट और आप इस तरह की समस्या नहीं होगी। विम जैसे कुछ संपादक आपकी पूरी फ़ाइल को एक शॉर्टकट (gg=G विम में) के साथ इंडेंट कर सकते हैं।

हालांकि, OnlineUser.findOne() असीमित रूप से असीमित है। इसलिए यदि आप कॉल को सही स्थान पर ले जाते हैं तो यह काम नहीं करेगा। इसे हल करने के तरीके पर ShadowCloud's answer देखें।

+0

के रूप में मैं एक समाधान के लिए परीक्षण किया गया है, मैं पहले से ही कोड विज्ञापन पाश के अंत डालने की कोशिश की, तुम क्यों नहीं एक फर्क – Jeroen

+0

नहीं है 'res.render (...) 'कॉल ** ** के बाद ** foreach के अंत के बाद लेकिन पहले कॉलबैक के अंदर डाल दें। फोरैच समाप्त होने के बाद रीडायरेक्ट को निष्पादित किया जाएगा। – jsbeckr

+0

@graydsl मुझे नहीं पता कि आप कहां से सही हैं लेकिन मुझे लगता है कि मैंने ज्यादातर स्थानों की कोशिश की है, यह लूपिंग से पहले प्रस्तुत करना जारी रखता है: o – Jeroen

6

मैं अपने प्रोजेक्ट के लिए async पैकेज जोड़ने और async.each() लिए foreach() को बदलकर कुछ इसी तरह हल करने में सक्षम था। लाभ यह है कि यह एप्लिकेशन के अन्य हिस्सों के लिए सिंक्रनाइज़ेशन करने का एक मानक तरीका प्रदान करता है।

कुछ अपनी परियोजना के लिए इस तरह:

function onlineFriends(req, res) { 
    var onlinefriends = new Array(); 
    onlinefriends.push("mark"); 

    FriendList.findOne({owner: req.session.username}, function (err, friendlist) { 
    async.each(friendlist.friends, function(friend, callback) { 
     OnlineUser.findOne({userName: friend}, function (err, onlineFriend) { 
     if (onlineFriend != null) { 
      onlinefriends.push(onlineFriend.userName); 
      console.log("a loop"); 
     } 
     callback(); 
     }); 
    }, function(err) { 
     console.log("online friends: " + onlinefriends); 
     console.log("redirecting"); 
     res.render('index', { // this is still inside the forEach function 
      friendlist: friendlist.friends, 
      onlinefriendlist: onlinefriends, 
      username: req.session.username 
     }); 
    }); 
    }); 
} 
+0

यह आगे बढ़ने के लिए सबसे अच्छा समाधान है, कॉलबैक आग के बारे में चिंता करने या आदेश देने के लिए कोई चर नहीं है। धन्यवाद! – sidonaldson

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

  • कोई संबंधित समस्या नहीं^_^