2016-12-20 7 views
12

Кажись, возникли некоторые проблемы, включающие асинхр/ждут с .reduce(), например так:JavaScript массив .reduce с асинхронной/Await

const data = await bodies.reduce(async(accum, current, index) => { 
    const methodName = methods[index] 
    const method = this[methodName] 
    if (methodName == 'foo') { 
    current.cover = await this.store(current.cover, id) 
    console.log(current) 
    return { 
     ...accum, 
     ...current 
    } 
    } 
    return { 
    ...accum, 
    ...method(current.data) 
    } 
}, {}) 
console.log(data) 

Объект data регистрируется перед в this.store .. не завершится .

Я знаю, что вы можете использовать Promise.all с асинхронными петлями, но применимо ли оно к .reduce()?

ответ

21

Проблема в том, что ваши значения аккумулятора являются обещаниями - это возвращаемые значения async function s. Для того, чтобы получить последовательную оценку (и все, кроме последней итерации ожидаться на всех), вам нужно использовать

const data = await array.reduce(async (accumP, current, index) => { 
    const accum = await accumP; 
    … 
}, Promise.resolve(…)); 

вышесказанное, для async/await я бы вообще рекомендую use plain loops instead of array iteration methods, они более производительным и часто проще.

+1

Спасибо за ваши советы в конце , Я закончил использовать только простой цикл для того, что я делал, и это были те же строки кода, но гораздо проще читать ... –

+0

'initialValue'' reduce' не обязательно должен быть 'Promise ', однако, в большинстве случаев прояснить намерение. – EECOLOR

+0

@EECOLOR Это должно быть. Мне очень не нравится «ждать», чтобы придать простое значение обещанию – Bergi

1

Вы можете обернуть всю свою карту/уменьшить блоки итератора в свой собственный Promise.resolve и ждать этого завершения. Проблема, однако, в том, что аккумулятор не содержит результирующих данных/объектов, которые вы ожидаете на каждой итерации. Благодаря внутренней цепочке асинхронных/ожидающих/обещаний, аккумулятор будет фактическим самим обещанием, которое, вероятно, еще не разрешилось, несмотря на использование ключевого слова ожидания до вашего вызова в хранилище (что может заставить вас поверить, что итерация на самом деле не будет вернуться, пока этот вызов не завершится и аккумулятор обновляется.

Хотя это не самое элегантное решение, один вариант у вас есть, чтобы переместить ваши данные переменную объекта из области видимости и назначить его в качестве пусть так, что надлежащее связывание и мутация могут произойти. Затем обновите этот объект данных внутри вашего итератора при разрешении асинхронных/ожидающих/обещаний.

/* allow the result object to be initialized outside of scope 
    rather than trying to spread results into your accumulator on iterations, 
    else your results will not be maintained as expected within the 
    internal async/await/Promise chain. 
*/  
let data = {}; 

await Promise.resolve(bodies.reduce(async(accum, current, index) => { 
    const methodName = methods[index] 
    const method = this[methodName]; 
    if (methodName == 'foo') { 
    // note: this extra Promise.resolve may not be entirely necessary 
    const cover = await Promise.resolve(this.store(current.cover, id)); 
    current.cover = cover; 
    console.log(current); 
    data = { 
     ...data, 
     ...current, 
    }; 
    return data; 
    } 
    data = { 
    ...data, 
    ...method(current.data) 
    }; 
    return data; 
}, {}); 
console.log(data); 
0

Мне нравится ответ Берги, я думаю, что это правильный путь.

Я хотел бы также упомянуть библиотеку шахты, называемой Awaity.js

который позволяет вам легко использовать функции, такие как reduce, map & filter с async/await:

import reduce from 'awaity/reduce'; 

const posts = await reduce([1,2,3], async (posts, id) => { 
    const res = await fetch('/api/posts/' + id); 
    const post = res.json(); 

    return { 
    ...posts, 
    [id]: post 
    } 
}, {}) 

posts // { 1: { ... }, 2: { ... }, 3: { ... } } 
Смежные вопросы