我正在使用meteor.js和MongoDB构建一个应用程序,我有一个关于cursor.forEach()的问题。 我想在每次forEach迭代的开始检查一些条件,然后跳过元素,如果我不需要对它进行操作,这样我可以节省一些时间。

这是我的代码:

// Fetch all objects in SomeElements collection
var elementsCollection = SomeElements.find();
elementsCollection.forEach(function(element){
  if (element.shouldBeProcessed == false){
    // Here I would like to continue to the next element if this one 
    // doesn't have to be processed
  }else{
    // This part should be avoided if not neccessary
    doSomeLengthyOperation();
  }
});

我知道我可以使用cursor.find().fetch()将光标转向数组,然后使用常规for循环迭代元素,并正常使用continue和break,但我感兴趣的是,如果在forEach()中使用类似的东西。


forEach()的每次迭代都将调用您提供的函数。为了在任何给定的迭代中停止进一步的处理(并继续下一项),你只需要在适当的点从函数返回:

elementsCollection.forEach(function(element){
  if (!element.shouldBeProcessed)
    return; // stop processing this iteration

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});

在我看来,实现这一点的最佳方法是使用filter方法,因为在forEach块中返回是没有意义的;在你的代码片段中有一个例子:

// Fetch all objects in SomeElements collection
var elementsCollection = SomeElements.find();
elementsCollection
.filter(function(element) {
  return element.shouldBeProcessed;
})
.forEach(function(element){
  doSomeLengthyOperation();
});

这将缩小您的elementsCollection,只保留应该处理的过滤元素。


下面是一个用for of和continue代替forEach的解决方案:


let elementsCollection = SomeElements.find();

for (let el of elementsCollection) {

    // continue will exit out of the current 
    // iteration and continue on to the next
    if (!el.shouldBeProcessed){
        continue;
    }

    doSomeLengthyOperation();

});

如果你需要在循环中使用在forEach中不起作用的异步函数,这可能会更有用一些。例如:


(async fuction(){

for (let el of elementsCollection) {

    if (!el.shouldBeProcessed){
        continue;
    }

    let res;

    try {
        res = await doSomeLengthyAsyncOperation();
    } catch (err) {
        return Promise.reject(err)
    }

});

})()

利用javascript进行短路计算。如果埃尔。shouldBeProcessed返回true, doSomeLengthyOperation

elementsCollection.forEach( el => 
  el.shouldBeProcessed && doSomeLengthyOperation()
);

使用continue语句而不是return来跳过JS循环中的迭代。


简单的答案是在forEach循环中放入一个return语句将为您完成工作,就像@nnnnnn所说的那样,

elementsCollection.forEach(function(element){
  if (!element.shouldBeProcessed)
    return; // stop processing this iteration

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});

但如果你想知道这个问题的深层答案那就来找我吧。

假设你不知道forEach循环的实现,那么看看下面的forEach循环的实现,这正是ECMA-262中为forEach循环指定的第5版。

Source Array.prototype.forEach() - JavaScript | MDN

if (!Array.prototype['forEach']) {

  Array.prototype.forEach = function(callback, thisArg) {

    if (this == null) { throw new TypeError('Array.prototype.forEach called on null or undefined'); }

    var T, k;
    // 1. Let O be the result of calling toObject() passing the
    // |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get() internal
    // method of O with the argument "length".
    // 3. Let len be toUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If isCallable(callback) is false, throw a TypeError exception.
    // See: https://es5.github.com/#x9.11
    if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); }

    // 5. If thisArg was supplied, let T be thisArg; else let
    // T be undefined.
    if (arguments.length > 1) { T = thisArg; }

    // 6. Let k be 0
    k = 0;

    // 7. Repeat, while k < len
    while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }
    // 8. return undefined
  };
}

你真的不需要理解上面代码的每一行,因为我们感兴趣的是while循环,

while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }

如果你注意到的话,会有一个语句回调。call(T, KValue, K, O)再次强调,我们对给call()方法的参数不感兴趣,我们真正感兴趣的是回调绑定,这是一个你在javascript中给forEach循环的函数。call方法只调用被调用的对象(javascript函数),并单独提供this值和参数。

如果你不明白什么是调用,那么看看Function.prototype.Call() - JavaScript | MDN。

Just think about this if at any point your function that is callback in this case returns at any point the loop will be updated as usual. The loop doesn't care about if the callback function has performed each and every step given to it or not if the control has returned to the loop the loop has to do its job. Every time the loop is updated the callback is called with new set of values as you can see there T, KValue, K, O are changing every time the loop updates, so if at any point you return from your function i.e., callback you are just handing the control to the loop you are called in no matter at what point you return from your function, if you want to skip some operations inside of your function at a given condition then just put the return statement before those statements you want to skip.

这就是在forEach循环中跳过迭代的方法。


如果你在使用经典的for循环,不想使用continue,你可以在里面使用self- execute函数,并使用return来模仿continue行为:

for (let i = 0; i < 10; i++) {
    (() => {
        if (i > 5) return;
        console.log("no.", i)
    })();
}

console.log("exited for loop")

输出:

[LOG]: "no.",  0 
[LOG]: "no.",  1 
[LOG]: "no.",  2 
[LOG]: "no.",  3 
[LOG]: "no.",  4 
[LOG]: "no.",  5 
[LOG]: "exited for loop"