Как остановить Javascript для каждого?
Я играю с nodejs и mongoose - пытаюсь найти конкретный комментарий в глубоких комментариях, вложенных в рекурсивные функции и foreach внутри. Есть ли способ остановить nodejs для каждого? Как я понимаю, каждая итерация forEach - это функция, и я не могу просто сделать "break", только "return", но это не остановит foreach.
function recurs(comment){
comment.comments.forEach(function(elem){
recurs(elem);
//if(...) break;
});
}
13 ответов
Вы не можете оторваться от forEach
, Хотя я могу придумать три способа подделать его.
1. Гадкий путь: передайте второй аргумент forEach
использовать в качестве контекста и хранить там логическое значение, а затем использовать if
, Это выглядит ужасно.
2. Спорный путь: окружить все это в try-catch
заблокировать и выбросить исключение, когда вы хотите сломать. Это выглядит довольно плохо и может повлиять на производительность, но может быть инкапсулировано.
3. Веселый путь: использовать every()
,
['a', 'b', 'c'].every(function(element, index) {
// Do your thing, then:
if (you_want_to_break) return false
else return true
})
Ты можешь использовать some()
вместо этого, если вы предпочитаете return true
сломать.
Вырваться из Array#forEach невозможно. (Вы можете проверить исходный код, который реализует его в Firefox на связанной странице, чтобы подтвердить это.)
Вместо этого вы должны использовать нормальный for
цикл:
function recurs(comment) {
for (var i = 0; i < comment.comments.length; ++i) {
var subComment = comment.comments[i];
recurs(subComment);
if (...) {
break;
}
}
}
(или, если вы хотите быть немного умнее и comment.comments[i]
всегда объект:)
function recurs(comment) {
for (var i = 0, subComment; subComment = comment.comments[i]; ++i) {
recurs(subComment);
if (...) {
break;
}
}
}
forEach не ломается по возвращении, есть ужасные решения, чтобы получить эту работу, но я предлагаю не использовать ее, вместо этого попробуйте использовать Array.prototype.some
или же Array.prototype.every
var ar = [1,2,3,4,5];
ar.some(function(item,index){
if(item == 3){
return true;
}
console.log("item is :"+item+" index is : "+index);
});
В некоторых случаях Array.some
вероятно выполнит требования.
Как уже отмечали другие, вы не можете отменить forEach
цикл, но вот мое решение:
ary.forEach(function loop(){
if(loop.stop){ return; }
if(condition){ loop.stop = true; }
});
Конечно, это на самом деле не прерывает цикл, а просто предотвращает выполнение кода на всех элементах, следующих за "разрывом"
Я думаю, вы хотите использовать Array.prototype. find Find сломается, когда найдет ваше конкретное значение в массиве.
var inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5}
];
function findCherries(fruit) {
return fruit.name === 'cherries';
}
console.log(inventory.find(findCherries));
// { name: 'cherries', quantity: 5 }
Вы можете использовать Лодаш forEach
Функция, если вы не возражаете против использования сторонних библиотек.
Пример:
var _ = require('lodash');
_.forEach(comments, function (comment) {
do_something_with(comment);
if (...) {
return false; // Exits the loop.
}
})
var f = "how to stop Javascript forEach?".split(' ');
f.forEach(function (a,b){
console.info(b+1);
if (a == 'stop') {
console.warn("\tposition: \'stop\'["+(b+1)+"] \r\n\tall length: " + (f.length));
f.length = 0; //<--!!!
}
});
Array.forEach
не может быть сломан и используя try...catch
или хакерские методы, такие как Array.every
или же Array.some
только усложнит понимание вашего кода. Есть только два решения этой проблемы:
1) использовать старый for
Цикл: это будет наиболее совместимое решение, но его может быть очень трудно прочитать, если его часто использовать в больших блоках кода:
var testArray = ['a', 'b', 'c'];
for (var key = 0; key < testArray.length; key++) {
var value = testArray[key];
console.log(key); // This is the key;
console.log(value); // This is the value;
}
2) использовать новую ECMA6 (спецификацию 2015 года) в случаях, когда совместимость не является проблемой. Обратите внимание, что даже в 2016 году только несколько браузеров и IDE предлагают хорошую поддержку для этой новой спецификации. Хотя это работает для итерируемых объектов (например, массивов), если вы хотите использовать это для не итерируемых объектов, вам нужно будет использовать Object.entries
метод. Этот метод практически не доступен по состоянию на 18 июня 2016 года, и даже Chrome требует специальный флаг, чтобы включить его: chrome://flags/#enable-javascript-harmony
, Для массивов вам все это не понадобится, но совместимость остается проблемой:
var testArray = ['a', 'b', 'c'];
for (let [key, value] of testArray.entries()) {
console.log(key); // This is the key;
console.log(value); // This is the value;
}
3) Многие согласны с тем, что ни первый, ни второй вариант не являются хорошими кандидатами. Пока вариант 2 не станет новым стандартом, большинство популярных библиотек, таких как AngularJS и jQuery, предлагают свои собственные методы цикла, которые могут превосходить все, что доступно в JavaScript. Также для тех, кто еще не использует эти большие библиотеки и ищет легковесные варианты, такие решения могут быть использованы и будут почти на одном уровне с ECMA6 при сохранении совместимости со старыми браузерами.
Приведенный ниже код прервет цикл foreach при выполнении условия, ниже приведен пример
var array = [1,2,3,4,5];
var newArray = array.slice(0,array.length);
array.forEach(function(item,index){
//your breaking condition goes here example checking for value 2
if(item == 2){
array.length = array.indexOf(item);
}
})
array = newArray;
Вы можете выйти из цикла forEach, если перезапишите метод Array:
(function(){
window.broken = false;
Array.prototype.forEach = function(cb, thisArg) {
var newCb = new Function("with({_break: function(){window.broken = true;}}){("+cb.replace(/break/g, "_break()")+"(arguments[0], arguments[1], arguments[2]));}");
this.some(function(item, index, array){
newCb(item, index, array);
return window.broken;
}, thisArg);
window.broken = false;
}
}())
пример:
[1,2,3].forEach("function(x){\
if (x == 2) break;\
console.log(x)\
}")
К сожалению, с этим решением вы не можете использовать нормальный разрыв внутри ваших обратных вызовов, вы должны обернуть недопустимый код в строки, а встроенные функции не работают напрямую (но вы можете обойти это)
Счастливого взлома!
Почему бы не использовать простой возврат?
function recurs(comment){
comment.comments.forEach(function(elem){
recurs(elem);
if(...) return;
});
он вернется из функции 'recurs'. Я использую это так. Хотя это не будет нарушено для forEach, но для всей функции, в этом простом примере это может работать
JQuery обеспечивает each()
метод, а не forEach()
, Вы можете вырваться из each
вернувшись false
, forEach()
является частью стандарта ECMA-262, и единственный способ вырваться из этого, о котором я знаю, - это исключение.
function recurs(comment) {
try {
comment.comments.forEach(function(elem) {
recurs(elem);
if (...) throw "done";
});
} catch (e) { if (e != "done") throw e; }
}
Уродливо, но делает работу.