предисловие
Честно говоря, я не люблю использовать 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()
Это означает, что нет гарантии порядка выполнения асинхронных задач, если в них используется асинхронная функция.
альтернативный forEach
way
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] });
Поскольку мы использовали ключевое слово await
the в асинхронной функции, метод 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
для выхода из цикла или пропуска элемента. Если вам нужно выйти из цикла или пропустить элемент, вы должны использовать цикл for
a или другой вспомогательный метод break
или continue
statement.
Причина 4: forEach удаляет свои элементы, индекс нельзя обнулить
Мы не можем контролировать значение индекса в forEach
, оно будет увеличиваться автоматически только до тех пор, пока не превысит длину массива и не выйдет из цикла. Поэтому невозможно удалить себя для сброса индекса. Давайте рассмотрим простой пример:
let arr = [1,2,3,4] arr.forEach((item, index) => { console.log(item); // 1 2 3 4 index++; });
Причина 5: это указывает на проблему
В методе forEach()
a ключевое слово this
the относится к объекту, для которого вызывается метод. 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
. Итак, в стрелочной функции ключевое слово this
the относится к объекту, в котором функция определена. сокращение кода.
Причина 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