предисловие

Честно говоря, я не люблю использовать forEach, потому что некоторые из ошибок, которые он вызывает, всегда настолько непреднамеренны, подведите итоги причин, которые мне не нравятся.

Причина 1: не поддерживает обработку асинхронных функций

Сначала рассмотрим пример:

async function test() {
     let arr = [3, 2, 1]
     arr.forEach(async item => {
         const res = await mockSync(item)
         console. log(res)
     })
     console. log('end')
}

function mockSync(x) {
     return new Promise((resolve, reject) => {
         setTimeout(() => {
                 resolve(x)
         }, 1000 * x)
     })
}
test()
The result we expect is:
3
2 
1
end

but actually outputs:
end
1
2
3

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

альтернативный forEachway

1. Способ 1

Вы можете использовать, например. map(), filter(), reduce()и т. д. которые поддерживают возврат в функциях Promise и будут ждать выполнения всех промисов.

Пример кода для использования map()и Promise.all()для обработки асинхронных функций выглядит следующим образом:

const arr = [1, 2, 3, 4, 5];
async function asyncFunction(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num * 2);
    }, 1000);
  });
}
const promises = arr.map(async (num) => {
  const result = await asyncFunction(num);
  return result;
});
Promise.all(promises).then((results) => {
  console.log(results); // [2, 4, 6, 8, 10]
});

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

2. Способ 2

использует цикл for для обработки асинхронных функций

const arr = [1, 2, 3, 4, 5];

async function asyncFunction(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num * 2);
    }, 1000);
  });
}

async function processArray() {
  const results = [];
  for (let i = 0; i < arr.length; i++) {
    const result = await asyncFunction(arr[i]);
    results.push(result);
  }
  console.log(results); // [2, 4, 6, 8, 10]
}

processArray();

Причина 2: Невозможно отловить ошибки в асинхронных функциях

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

Причина 3: Нет другого способа прервать или выйти из цикла, кроме создания исключения

forEach()Method не поддерживает использование операторов breakили continue для выхода из цикла или пропуска элемента. Если вам нужно выйти из цикла или пропустить элемент, вы должны использовать цикл fora или другой вспомогательный метод breakили continuestatement.

Причина 4: forEach удаляет свои элементы, индекс нельзя обнулить

Мы не можем контролировать значение индекса в forEach, оно будет увеличиваться автоматически только до тех пор, пока не превысит длину массива и не выйдет из цикла. Поэтому невозможно удалить себя для сброса индекса. Давайте рассмотрим простой пример:

let arr = [1,2,3,4]
arr.forEach((item, index) => {
    console.log(item); // 1 2 3 4
    index++;
});

Причина 5: это указывает на проблему

В методе forEach()a ключевое слово thisthe относится к объекту, для которого вызывается метод. thisОднако определение области действия ключевых слов может быть проблематичным при использовании в качестве аргументов обычных функций или стрелочных функций . В стрелочной функции ключевое слово this относится к объекту, в котором функция была определена. В обычной функции ключевое слово this относится к объекту, вызвавшему функцию. Если вам нужно убедиться, что область действия ключевого слова this верна, вы можете использовать метод bind() для привязки области действия функции. Вот пример проблемы с областью действия ключевого слова forEach()в методе this:

const obj = {
  name: "Alice",
  friends: ["Bob", "Charlie", "Dave"],
  printFriends: function () {
    this.friends.forEach(function (friend) {
      console.log(this.name + " is friends with " + friend);
    });
  },
};
obj.printFriends();

В этом примере мы определяем объект obj с именем , у которого есть метод printFriends(). В методе printFriends()the мы используем метод forEach()the для перебора friendsмассива и используем нормальную функцию для вывода имени каждого друга и свойств objобъекта name. Однако, когда мы запускаем этот код, вывод:

undefined is friends with Bob
undefined is friends with Charlie
undefined is friends with Dave

Это связано с тем, что forEach()когда в методе используется обычная функция, областью действия функции является не printFriends()объект, вызывающий метод, а глобальная область. Таким образом, свойства объекта не могут быть доступны внутри этой функции obj.

Чтобы обойти это, вы можете использовать bind()methods для привязки области действия функции или использовать стрелочные функции для определения функций обратного вызова. Вот bind()пример кода, в котором используется метод для решения проблемы:

const obj = {
   name: "Alice",
   friends: ["Bob", "Charlie", "Dave"],
   printFriends: function () {
     this.friends.forEach(
       function (friend) {
         console.log(this.name + " is friends with " + friend);
       }.bind(this) // Use the bind() method to bind the scope of the function
     );
   },
};
obj. printFriends();

В этом примере мы используем bind()methods для привязки области действия функции, которая привязывает область действия функции к objобъекту. После запуска кода вывод:

Alice is friends with Bob 
Alice is friends with Charlie 
Alice is friends with Dave

Используя bind()методы для привязки области действия функции, мы можем правильно получить доступ к objсвойствам объекта.

Другой обходной путь — использовать стрелочные функции. Поскольку у стрелочной функции нет собственного this, она наследует область видимости, в которой находится this. Итак, в стрелочной функции ключевое слово thisthe относится к объекту, в котором функция определена. сокращение кода.

Причина 6: производительность forEach ниже, чем для цикла for

for: Цикл for не имеет дополнительного стека вызовов функций и контекста, поэтому его реализация является самой простой.
forEach: ForEach, его сигнатура функции содержит параметры и контекст, поэтому производительность будет ниже, чем for цикла.

Причина 7: удаленные или неинициализированные элементы будут пропущены

// skip uninitialized values
const array = [1, 2, /* empty */, 4];
let num = 0;

array. forEach((ele) => {
   console. log(ele);
   num++;
});

console.log("num:",num);

//  1
//  2 
// 4
// num: 3

// skip deleted values
const words = ['one', 'two', 'three', 'four'];
words. forEach((word) => {
   console. log(word);
   if (word === 'two') {
   // When the item containing the value `two` is reached, the first item of the entire array is removed
   // This causes all remaining items to be moved up one position. Since the element `four` is at the beginning of the array, `three` is skipped.
     words.shift(); //'one' will be removed from the array
   }
}); // one // two // four

console.log(words); // ['two', 'three', 'four']

Причина 8: использование forEach не изменит исходный массив

forEach() При вызове исходный массив, то есть массив, вызвавший его, не будет изменен. Но этот объект может быть изменен переданной функцией обратного вызова.

// example one
const array = [1, 2, 3, 4];
array.forEach(ele => { ele = ele * 3 })
console.log(array); // [1,2,3,4]

// Solution, change the original array
const numArr = [33,4,55];
numArr.forEach((ele, index, arr) => {
     if (ele === 33) {
         arr[index] = 999
     }
})
console.log(numArr); // [999, 4, 55]

// Example 2
const changeItemArr = [{
     name: 'wxw',
     age: 22
}, {
     name: 'wxw2',
     age: 33
}]
changeItemArr.forEach(ele => {
     if (ele.name === 'wxw2') {
         ele = {
             name: 'change',
             age: 77
         }
     }
})
console.log(changeItemArr); // [{name: "wxw", age: 22},{name: "wxw2", age: 33}]

// solution
const allChangeArr = [{ name: 'wxw', age: 22}, { name: 'wxw2', age: 33}]
allChangeArr.forEach((ele, index, arr) => {
     if (ele.name === 'wxw2') {
         arr[index] = {
             name: 'change',
             age: 77
         }
     }
})
console.log(allChangeArr); // // [{name: "wxw", age: 22},{name: "change", age: 77}]
copy code

Что ж, воспользуюсь for...ofвместоforEach