как остановить Javascript forEach? [дубликат]


этот вопрос уже есть ответ здесь:

Я играю с nodejs и мангустом-пытаюсь найти конкретный комментарий в глубоких комментариях, вложенных с рекурсивной функцией и foreach внутри. Есть ли способ, чтобы остановить nodejs цикле foreach? Как я понимаю каждый forEach итерация-это функция,и я не могу просто сделать "перерыв", только "возврат", но это не остановит foreach.

function recurs(comment){
    comment.comments.forEach(function(elem){
        recurs(elem);
        //if(...) break;
    });
}
13 274

13 ответов:

вы не можете оторваться от forEach. Однако я могу придумать три способа подделать его.

1. Уродливый Путь: передать второй аргумент forEach to использовать в качестве контекста, и хранить логическое значение там, а затем использовать 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 для того чтобы сломать.

вырвавшись из массив#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 петли, но вот мое решение:

ary.forEach(function loop(){
    if(loop.stop){ return; }

    if(condition){ loop.stop = true; }
});

конечно, это на самом деле не нарушает цикл, он просто предотвращает выполнение кода на всех элементах после "break"

в некоторых случаях Array.some будет, вероятно, соответствовать требованиям.

Я думаю, вы хотите использовать массив.прототип.найти 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 }

Вы можете использовать lodash это 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 не ломается по возвращении, есть уродливые решения, чтобы получить эту работу, но я предлагаю не использовать его, вместо этого попробуйте использовать 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);
});

ниже код будет перерыв цикла 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, если перезаписать метод массива:

(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)\
}")

к сожалению, с этим решением вы не можете использовать обычный перерыв внутри ваших обратных вызовов, вы должны обернуть недопустимый код в строки и собственные функции не работают напрямую (но вы можете обойти это)

счастливый ломать!

Wy не использовать простой возврат?

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; }
}

некрасиво, но работает.