2015-01-12 12 views
22

लूप इंडेक्स (i) लूप इंडेक्स (i) एक लूप के भीतर प्रोटैक्टर का उपयोग करते समय मैं अपेक्षा करता हूं कि मैं अपेक्षा कर रहा हूं।लूप

लक्षण:

विफल: सूचकांक बाध्य से बाहर। सूचकांक में तत्व का उपयोग करने की कोशिश कर रहा है: 'एक्स', लेकिन केवल 'एक्स' तत्वों देखते हैं

या

सूचकांक स्थिर और हमेशा अंतिम मान

के बराबर है मेरे कोड

for (var i = 0; i < MAX; ++i) { 
    getPromise().then(function() { 
    someArray[i] // 'i' always takes the value of 'MAX' 
    }) 
} 

उदाहरण के लिए:

var expected = ['expect1', 'expect2', 'expect3']; 
var els = element.all(by.css('selector')); 
for (var i = 0; i < expected.length; ++i) { 
    els.get(i).getText().then(function(text) { 
    expect(text).toEqual(expected[i]); // Error: `i` is always 3. 
    }) 
} 

या

var els = element.all(by.css('selector')); 
for (var i = 0; i < 3; ++i) { 
    els.get(i).getText().then(function(text) { 
    if (text === 'should click') { 
     els.get(i).click(); // fails with "Failed: Index out of bound. Trying to access element at index:3, but there are only 3 elements" 
    } 
    }) 
} 

या

var els = element.all(by.css('selector')); 
els.then(function(rawelements) { 
    for (var i = 0; i < rawelements.length; ++i) { 
    rawelements[i].getText().then(function(text) { 
     if (text === 'should click') { 
     rawelements[i].click(); // fails with "Failed: Index out of bound. Trying to access element at index:'rawelements.length', but there are only 'rawelements.length' elements" 
     } 
    }) 
    } 
}) 
+1

प्रयास के लिए धन्यवाद - लेकिन यह क्लासिक क्लोजर-लूप समस्या है। –

+0

@ बेंजामिनग्रेनबाम हां यह क्लासिक क्लोजर-लूप समस्या है, और मैं जवाब में http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example संदर्भित करता हूं। हालांकि, मैंने इसे दो कारणों से खोला। 1) बहुत से लोगों को दोनों के बीच सहसंबंध का एहसास नहीं होता है क्योंकि कुछ लोग 'तत्व को समझ नहीं पाते हैं। विश्वासियों ने वादे वापस कर दिया है और 2) क्लोजर प्रोटैक्टर के लिए सबसे अच्छा समाधान नहीं है क्योंकि इसके लिए प्रोटैक्टर-विशिष्ट समाधान हैं - उत्तर देखें – hankduan

+2

रहस्य है मुझे मार रहा है! क्या दो कारण हैं? –

उत्तर

31

कारण यह हो रहा है क्योंकि चांदा वादों का उपयोग करता है।

पढ़ें https://github.com/angular/protractor/blob/master/docs/control-flow.md

वादे (अर्थात element(by...), element.all(by...)) उनके then कार्यों को अंजाम जब अंतर्निहित कीमत तैयार हो जाता है। इसका अर्थ यह है कि सभी वादों को पहले निर्धारित किया जाता है और फिर then फ़ंक्शन चलाए जाते हैं क्योंकि परिणाम तैयार हो जाते हैं।

आप कुछ इस तरह चलाते हैं:

for (var i = 0; i < 3; ++i) { 
    console.log('1) i is: ', i); 
    getPromise().then(function() { 
    console.log('2) i is: ', i); 
    someArray[i] // 'i' always takes the value of 3 
    }) 
} 
console.log('* finished looping. i is: ', i); 

क्या होता है कि getPromise().then(function() {...}) रिटर्न तुरंत, पहले वादा तैयार है और then अंदर समारोह को क्रियान्वित करने के बिना है। तो सबसे पहले लूप 3 बार से चलाता है, सभी getPromise() कॉल शेड्यूल करता है। फिर, जैसे-जैसे वादे हल होते हैं, संबंधित then एस चलाए जाते हैं।

1) i is: 0 // schedules first `getPromise()` 
1) i is: 1 // schedules second `getPromise()` 
1) i is: 2 // schedules third `getPromise()` 
* finished looping. i is: 3 
2) i is: 3 // first `then` function runs, but i is already 3 now. 
2) i is: 3 // second `then` function runs, but i is already 3 now. 
2) i is: 3 // third `then` function runs, but i is already 3 now. 

तो, आप चांदा छोरों में चला सकता हूँ:

सांत्वना कुछ इस तरह दिखाई हैं? सामान्य समाधान बंद है। देखें JavaScript closure inside loops – simple practical example

for (var i = 0; i < 3; ++i) { 
    console.log('1) i is: ', i); 
    var func = (function() { 
    var j = i; 
    return function() { 
     console.log('2) j is: ', j); 
     someArray[j] // 'j' takes the values of 0..2 
    } 
    })(); 
    getPromise().then(func); 
} 
console.log('* finished looping. i is: ', i); 

लेकिन यह पढ़ने के लिए अच्छा नहीं है। सौभाग्य से, आप प्रोटैक्टर फ़ंक्शन filter(fn), get(i), first(), last() का उपयोग भी कर सकते हैं, और तथ्य यह है कि expect इस से निपटने के लिए वादे लेने के लिए तैयार है।

पहले प्रदान किए गए उदाहरणों पर वापस जायें।

var expected = ['expect1', 'expect2', 'expect3']; 
var els = element.all(by.css('selector')); 
for (var i = 0; i < expected.length; ++i) { 
    expect(els.get(i).getText()).toEqual(expected[i]); // note, the i is no longer in a `then` function and take the correct values. 
} 

दूसरे और तीसरे उदाहरण के रूप में लिखा जा सकता है: के रूप में पहला उदाहरण में लिखा जा सकता

var els = element.all(by.css('selector')); 
els.filter(function(elem) { 
    return elem.getText().then(function(text) { 
    return text === 'should click'; 
    }); 
}).click(); 
// note here we first used a 'filter' to select the appropriate elements, and used the fact that actions like `click` can act on an array to click all matching elements. The result is that we can stop using a for loop altogether. 

दूसरे शब्दों में, चांदा कई पुनरावृति करने के लिए या तरीकों का उपयोग कर सकते तत्व i ताकि है आप डॉन loops और i के लिए उपयोग करने की आवश्यकता नहीं है। लेकिन अगर आपको लूप और i के लिए उपयोग करना चाहिए, तो आप बंद समाधान का उपयोग कर सकते हैं।

+2

इस समस्या को कई बार देखा है, चीजों को साफ़ करने के लिए धन्यवाद! अब हम कर सकते हैं इस पोस्ट का संदर्भ लें। – alecxe

+0

हाँ, मैंने पिछले हफ्ते में इस सटीक समस्या को दो बार देखा है और कई अन्य वादे से संबंधित प्रश्न हैं। उम्मीद है कि इससे लोगों को आम तौर पर वादे को और अधिक समझने में मदद मिलेगी। – hankduan

+1

"लूप काउंटर "मुद्दा वादे के लिए विशिष्ट नहीं है। * लूप में परिभाषित कोई भी * फ़ंक्शन काउंटर के टर्मिनल मान का शिकार होगा चाहे वह वादा कॉलबैक हो या नहीं। उदाहरण के लिए एक ईवेंट हैंडलर - एक ही सौदे पर विचार करें। –

2

हैंक ने इसका जवाब देने के लिए एक महान काम किया।
मैं इसे संभालने के लिए एक और त्वरित और गंदा तरीका भी नोट करना चाहता था। बस वादे सामान को कुछ बाहरी कार्यों में ले जाएं और इसे इंडेक्स पास करें।

उदाहरण के लिए आप कुछ इस तरह कर सकता है अगर आप अपने संबंधित सूचकांक (ElementArrayFinder से) पर पृष्ठ पर सभी सूची आइटम प्रवेश करना चाहते हैं:

var log_at_index = function (matcher, index) { 
    return $$(matcher).get(index).getText().then(function (item_txt) { 
     return console.log('item[' + index + '] = ' + item_txt); 
    }); 
    }; 

    var css_match = 'li'; 
    it('should log all items found with their index and displayed text', function() { 
    $$(css_match).count().then(function (total) { 
     for(var i = 0; i < total; i++) 
     log_at_index(css_match, i); // move promises to external function 
    }); 
    }); 

इस काम में आता है जब आप कुछ करने की ज़रूरत है तेजी से डिबगिंग & अपने स्वयं के उपयोग के लिए ट्विक करने में आसान है।