Javascript - передача асинхронного обратного вызова в Array.reduce () - PullRequest
1 голос
/ 23 апреля 2020

Я работаю над функцией, которая использует Array.reduce, и мне нужно добавить асинхронный вызов API внутри функции Reduce. Это требует, чтобы я использовал асин c функцию для обратного вызова, который я передаю в reduce, так как я использую await внутри функции для ожидания асинхронного вызова API.

У меня есть некоторые проблемы с написанием правильного сокращения. Вот как это работает (работает):

const result = records.reduce((array, currValue) => {
    //do stuff
    return array
}, [])

Вот что я пытался изменить на:

const result = records.reduce(async(array, currentValue) => {
    // do stuff
    someValue = await asyncCall(currentValue)
    array.add(someValue)
    return array
}, [])

Ошибка, которую я получаю: «Нет перегрузки, соответствует этому вызову» .

Мне кажется, это имеет смысл, так как Reduce принимает обратный вызов, который возвращает массив, а функции asyn c возвращают обратный вызов, а не массив. Но когда я читаю другие примеры того, как передать асин c функции в .reduce, они все, кажется, просто без проблем передают асин c функцию в Reduce.

Вот несколько ссылок, которые я посмотрел at:

https://advancedweb.hu/how-to-use-async-functions-with-array-reduce-in-javascript/

JavaScript массив. Сократить с асинхронным ожиданием

https://gyandeeps.com/array-reduce-async-await/

В тот момент, когда я объявляю функцию редукции в asyn c, я получаю ошибку, не соответствующую перегрузкам, которая имеет смысл для меня. Я не уверен, как это работает для других людей.

Ответы [ 2 ]

1 голос
/ 23 апреля 2020

Первое: reduce, вероятно, не лучший инструмент для этого. Похоже, вы просто добавляете записи в массив. reduce слишком сложен для этой задачи, особенно , если вы делаете что-то асинхронное. Вместо этого конструкция цикла, которую вы можете использовать в функции async, намного, намного проще.

Я начну с reduce, затем go с конструкции цикла.

reduce работает синхронно . Если вы передадите функцию async в качестве ее обратного вызова, обещание, которое возвращает функция, будет значением аккумулятора, увиденным при следующем обратном вызове. Таким образом, если один из шагов в операции сокращения должен быть асинхронным и возвращать обещание, каждый шаг после того, как он должен быть асинхронным, возвращает обещание (для простоты лучше сделать так, чтобы каждый шаг возвращал обещание ); и результат reduce будет обещанием конечного значения, а не самого конечного значения. Вы не можете сделать асинхронный вызов синхронным и не можете заставить синхронную операцию (reduce) ожидать асинхронного результата.

Итак, все ваши обратные вызовы будут иметь дело с обещаниями. Это будет выглядеть примерно так:

const result = await records.reduce(async(arrayPromise, currentValue) => {
// −−−−−−−−−−−−^^^^^−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^
    const array = await arrayPromise // <=====
    // do stuff
    someValue = await asyncCall(currentValue)
    array.push(someValue) // <==== `push` rather than `add`, presumably
    return array
}, Promise.resolve([]))
// ^^^^^^^^^^^^^^^^−−^

Конечно, поскольку здесь используется await, оно должно быть в функции async. В противном случае:

records.reduce(async(arrayPromise, currentValue) => {
    const array = await arrayPromise // <=====
    // do stuff
    someValue = await asyncCall(currentValue)
    array.push(someValue)
    return array
}, Promise.resolve([]))
.then(result => {
    // ...use `result` here
})
.catch(error => {
    // ...handle/report error here...
})

Вам лучше использовать циклическую конструкцию, которая изначально поддерживает участие в функции async:

const result = []
for (const currentValue of records) {
    someValue = await asyncCall(currentValue)
    result.push(someValue)
}
// ...use `result` here...

или даже

const result = []
for (const currentValue of records) {
    result.push(await asyncCall(currentValue))
}
// ...use `result` here...

Если вам нужно сделать это в функции, которая не является функцией async, вы будете явно иметь дело с обещанием, которое будет выглядеть следующим образом:

(async () => {
    const result = []
    for (const currentValue of records) {
        result.push(await asyncCall(currentValue))
    }
    return result
})()
.then(result => {
    // ...use `result` here
})
.catch(error => {
    // ...handle/report error here...
})
0 голосов
/ 24 апреля 2020

Я думаю, что самая простая вещь будет выглядеть следующим образом

const promises = records.reduce((array, currentValue) => {
    // do stuff
    someValue = asyncCall(currentValue)
    array.add(someValue)
    return array
}, [])

const results= Promise.all(promises);

Если вариант использования вашей функции уменьшения сложнее, пожалуйста, оставьте больше кода или создайте песочницу

...