开发者

how to stop Javascript forEach? [duplicate]

This question already has answers here: Short circuit Array.forEach like calling break (30 answers) Closed 5 years ago.

I'm playing with Node.js and Mongoose — trying to find specific comment in deep comments nesting with recursive function and forEach within. Is there a way to stop Node.js forEach? As I understand every forEach iteration is a function and and I can't just do break, only return开发者_StackOverflow社区 but this won't stop forEach.

function recurs(comment) {
    comment.comments.forEach(function(elem) {

        recurs(elem);

        //if(...) break;

    });
}


You can't break from a forEach. I can think of three ways to fake it, though.

1. The Ugly Way: pass a second argument to forEach to use as context, and store a boolean in there, then use an if. This looks awful.

2. The Controversial Way: surround the whole thing in a try-catch block and throw an exception when you want to break. This looks pretty bad and may affect performance, but can be encapsulated.

3. The Fun Way: use every().

['a', 'b', 'c'].every(function(element, index) {
  // Do your thing, then:
  if (you_want_to_break) return false
  else return true
})

You can use some() instead, if you'd rather return true to break.


Breaking out of Array#forEach is not possible. (You can inspect the source code that implements it in Firefox on the linked page, to confirm this.)

Instead you should use a normal for loop:

function recurs(comment) {
    for (var i = 0; i < comment.comments.length; ++i) {
        var subComment = comment.comments[i];
        recurs(subComment);
        if (...) {
            break;
        }
    }
}

(or, if you want to be a little more clever about it and comment.comments[i] is always an object:)

function recurs(comment) {
    for (var i = 0, subComment; subComment = comment.comments[i]; ++i) {
        recurs(subComment);
        if (...) {
            break;
        }
    }
}


forEach does not break on return, there are ugly solutions to get this work but I suggest not to use it, instead try to use Array.prototype.some or 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);
});


In some cases Array.some will probably fulfil the requirements.


As others have pointed out, you can't cancel a forEach loop, but here's my solution:

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

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

Of course this doesn't actually break the loop, it just prevents code execution on all the elements following the "break"


I guess you want to use Array.prototype.find Find will break itself when it finds your specific value in the array.

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 }


You can use Lodash's forEach function if you don't mind using 3rd party libraries.

Example:

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 cannot be broken and using try...catch or hacky methods such as Array.every or Array.some will only make your code harder to understand. There are only two solutions of this problem:

1) use a old for loop: this will be the most compatible solution but can be very hard to read when used often in large blocks of code:

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) use the new ECMA6 (2015 specification) in cases where compatibility is not a problem. Note that even in 2016, only a few browsers and IDEs offer good support for this new specification. While this works for iterable objects (e.g. Arrays), if you want to use this on non-iterable objects, you will need to use the Object.entries method. This method is scarcely available as of June 18th 2016 and even Chrome requires a special flag to enable it: chrome://flags/#enable-javascript-harmony. For Arrays, you won't need all this but compatibility remains a problem:

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) A lot of people would agree that neither the first or second option are good candidates. Until option 2 becomes the new standard, most popular libraries such as AngularJS and jQuery offer their own loop methods which can be superior to anything available in JavaScript. Also for those who are not already using these big libraries and that are looking for lightweight options, solutions like this can be used and will almost be on par with ECMA6 while keeping compatibility with older browsers.


Below code will break the foreach loop once the condition is met, below is the sample example

    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;


You can break from a forEach loop if you overwrite the Array method:

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

}())

example:

[1,2,3].forEach("function(x){\
    if (x == 2) break;\
    console.log(x)\
}")

Unfortunately with this solution you can't use normal break inside your callbacks, you must wrap invalid code in strings and native functions don't work directly (but you can work around that)

Happy breaking!


Wy not use plain return?

function recurs(comment){
comment.comments.forEach(function(elem){
    recurs(elem);
    if(...) return;
});

it will return from 'recurs' function. I use it like this. Althougth this will not break from forEach but from whole function, in this simple example it might work


jQuery provides an each() method, not forEach(). You can break out of each by returning false. forEach() is part of the ECMA-262 standard, and the only way to break out of that that I'm aware of is by throwing an exception.

function recurs(comment) {
  try {
    comment.comments.forEach(function(elem) {
      recurs(elem);
      if (...) throw "done";
    });
  } catch (e) { if (e != "done") throw e; }
}

Ugly, but does the job.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜