"Продовжити" в cursor.forEach ()


279

Я будую додаток за допомогою meteor.js та MongoDB, і у мене є питання щодо cursor.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-loop для перебору елементів і використовувати продовження та перерву нормально, але мені цікаво, чи є щось подібне для використання у forEach ( ).

Відповіді:


561

Кожна ітерація forEach()заповіту викликає функцію, яку ви надали. Щоб зупинити подальшу обробку в межах будь-якої заданої ітерації (і продовжити наступний елемент), вам просто потрібно виконати returnфункцію у відповідній точці:

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

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

18
Чи знаєте ви, можливо, що може бути "перервою", тоді, якщо продовжувати, це просто "повернення".
Drag0

5
Я не використовую MongoDB, тому не читав його документацію, але можливо, це return false;було б еквівалентом break;(як це для .each()циклу jQuery ). Звичайно, хто реалізував MongoDB, .forEach()можливо, мав інші ідеї ...
nnnnnn

9
@ Drag0 Ви можете використовувати .some () як заміну для .forEach (), що дозволяє повернути false для розриву циклу.
Андрій

6
@Andrew Ви можете використовувати some, просто пам’ятайте, що ви неправильно використовуєте (або творчо використовуєте) функцію, яка мала призначити, чи відповідає якийсь із елементів умові. Начебто, коли я бачу, як люди використовують mapі ігнорують результат (вони повинні були використовувати forEach). Це семантика, людям доведеться шукати двічі, щоб знати, чому ви користуєтесь, someколи насправді не хвилює результат
Хуан Мендес

1
@Andrew чудова порада, однак саме ця return true
порція перерве

11

На мою думку, найкращий підхід для досягнення цього filter методом, оскільки повертатись у forEachблоці безглуздо ; для прикладу вашого фрагмента:

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

Це звузить ваш elementsCollectionі просто збереже filtredелементи, які слід обробити.


3
Це повторить знайдені елементи двічі, один раз у filterдругому, forEachякщо це велика колекція, це буде дуже неефективно
Dementic

1
Ви маєте рацію, але я не думаю, що це велика справа, оскільки часова складність цього могла O(2n)б вважатися такою O(n).
Рамі Тамер

2
Зважаючи на те, що SO використовується іншими, а не тільки ОП, розміщення рішення лише з метою його опублікування створює більше шкоди, ніж користі. Відповідь, наведена вище, робить це в одній ітерації, і це rightспосіб зробити це.
Дементік

Зауважте, що колекція OP - це не масив, це об’єкт курсору DBM Mongo, який, схоже, не має .filter()методу, тому вам доведеться викликати його .toArray()метод, перш ніж ви могли.filter()
nnnnnn

7

Ось рішення з використанням 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)
    }

});

})()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.