Как сохранить значения массива внутри асинхронного цикла? JavaScript

У меня есть код ниже, который использует API для получения данных о клиентах. Проблема в том, что когда цикл переходит ко второму индексу, customerIds сохраняет значение из предыдущего index (см. журнал консоли ниже).

Кто-нибудь знает, как добиться этого правильно?


Вот мой код

let customerIds = [];

arrayChunks.forEach(async (chunkGroupIds, index) => {
    try {
        console.info('customerIds - before', index, customerIds)

        const checkStatusResult = await checkStatus(
            token,
            chunkGroupIds
        )

        chunkGroupIds.map((customerId) => {
            const found = checkStatusResult.response.data.find(
                (data) => customerId.toString() === data.customerId
            )

            if (found) {
                customerIds = [...customerIds, customerId]
            }
        })
        
        console.info('customerIds - after', index, customerIds)
    } catch (error) {
        ...
    }
})

console.info('customerIds - final', customerIds)

Журналы консоли: О проблеме может свидетельствовать печатаемый текст. Как мы видим, когда он перешел ко второму индексу, он не получил предыдущее значение из первого индекса.

customerIds - before 0 []
customerIds - after 0 [2,3,5]
customerIds - before 1 []
customerIds - after 1 []
... and so on

customerIds - final []

@appleapple я изначально использовал push, но это не сработало.

Johnny 21.03.2022 05:19
customerIds должно быть правильным после цикла. (нужно дождаться завершения)
apple apple 21.03.2022 05:19

где объявлено customerIds?

Rodrigo Rodrigues 21.03.2022 05:20

да я изначально думал, что ты кэшируешь customerIds, перечитал и это не так. (поэтому я удаляю комментарий для использования push)

apple apple 21.03.2022 05:20
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
4
37
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Используйте цикл for of вместо обратного вызова

let customerIds = [];
let index = 0;

for (const chunkGroupIds of arrayChunks) {
    try {
        console.info('customerIds - before', index, customerIds)

        const checkStatusResult = await checkStatus(
            token,
            chunkGroupIds
        )

        chunkGroupIds.map((customerId) => {
            const found = checkStatusResult.response.data.find(
                (data) => customerId.toString() === data.customerId
            )

            if (found) {
                customerIds.push(customerId);
            }
        })
        
        console.info('customerIds - after', index, customerIds)
    } catch (error) {
        ...
    } finally {
       index++;
    }
}

console.info('customerIds - final', customerIds)

теперь он отображает правильные значения .. я думаю, что это работает. У вас есть идеи, почему мой код выше не работал. или, может быть, это потому, что внутри функции async есть forEach, которую я использовал. Однако, если я удалю async, я получу ошибку машинописного текста.

Johnny 21.03.2022 05:36

Да, предыдущий не работал из-за реализации обратного вызова. For each метод возвращает обратный вызов для каждой итерации элемента массива, и каждый обратный вызов имеет свой собственный контекст выполнения, вы не можете использовать async await для ожидания завершения первой итерации, если у вас есть код async внутри обратного вызова. Каждая итерация выполняется независимо от другой.

Amir Saleem 21.03.2022 06:01

Это похоже на проблему асинхронности, может показаться, что промисы в массиве выполняются последовательно, но это не так. В цикле forEach все они выполняются одновременно.

Это может быть что-то хорошее в вашем случае, так как вам не нужно ждать суммы последовательного времени, где вы, вероятно, боретесь, находится в последней части, где вы хотите увидеть окончательный массив со всеми добавленными значениями, для этого Я рекомендую следующее:

const promises = arrayChunks.map(async (chunkGroupIds, index) => {
    try {
        console.info('customerIds - before', index, customerIds)

        const checkStatusResult = await checkStatus(
            token,
            chunkGroupIds
        )

        chunkGroupIds.map((customerId) => {
            const found = checkStatusResult.response.data.find(
                (data) => customerId.toString() === data.customerId
            )

            if (found) {
                customerIds = [...customerIds, customerId]
            }
        })
        
        console.info('customerIds - after', index, customerIds)
    } catch (error) {
        ...
    }
})

await Promise.all(promises); // Wait until all of them are finished
console.info('customerIds - final', customerIds)

Здесь утилита Promise.all позволяет дождаться окончания сбора промисов, функции автоматически сопоставляются с промисами благодаря ключевому слову async.

Если вам нужно, чтобы ваши обещания выполнялись последовательно, вы можете использовать подход, рекомендованный @amir-saleem. В противном случае мое предложение было бы лучше с точки зрения производительности.

я получил ошибку машинописного текста Никакая перегрузка не соответствует этому вызову. Перегрузка 1 из 2, '(значения: только для чтения unknown[] | []): Promise<[] | unknown[]>', выдало следующую ошибку.

Johnny 21.03.2022 05:37

Эта ошибка, похоже, не связана с кодом, который вы разместили изначально, можете ли вы предоставить больше контекста?

Daniel Rodríguez Meza 21.03.2022 05:44

Другие вопросы по теме