Причина это происходит потому, что транспортир использует обещания.
прочитанного 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
так, что вы не Не нужно использовать для петель и i
. Но если вы должны использовать для циклов и i
, вы можете использовать решение для закрытия.
Спасибо за усилие - но это классическая проблема замыкания. –
@BenjaminGruenbaum Да, это классическая проблема с замкнутым циклом, и в ответе я ссылаюсь на http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example. Однако я открыл это по двум причинам. 1) многие люди не понимают взаимосвязи между ними, потому что некоторые люди не понимают, что элементы elementFinders возвращают обещания и 2) закрытие - не лучшее решение для транспортира, так как для этого существуют решения, специфичные для протранслятора - см. Ответ – hankduan
. Убей меня! Какие две причины? –