How to use . reduce with async/await?

Asked

Viewed 176 times

4

In synchronous code I can have a .reduce to make a sum calculation (as in this simple example of a shopping list):

const tipos = ['leite', 'manteiga', 'pão'];
const precos = [23, 21, 32];

const buscarPreço = (tipo) => {
  const indice = tipos.indexOf(tipo);
  return precos[indice]
};

const somar = () => {
  const compras = ['leite', 'leite', 'manteiga', 'pão', 'pão', 'pão'];
  return compras.reduce((soma, tipo) => {
    const valor =  buscarPreço(tipo);
    return soma + valor;
  }, 0);
}
const soma = somar();
console.log(soma);

I tried to convert this into asynchronous code and I came across problems... appears in the result [object Promise]32. I did not understand the problem since the callback of the .reduce has async declared and inside I am "resolving" the precedent of the asynchronous part with const valor = await buscarPreço(tipo);... what I’m missing?

Asynchronous code (which gives error):

const tipos = ['leite', 'manteiga', 'pão'];
const precos = [23, 21, 32];

const buscarPreço = (tipo) => new Promise((res) => {
  const indice = tipos.indexOf(tipo);
  setTimeout(() => res(precos[indice]), 500);
});

const somar = async() => {
  const compras = ['leite', 'leite', 'manteiga', 'pão', 'pão', 'pão'];
  return compras.reduce(async(soma, tipo, i) => {
    const valor = await buscarPreço(tipo);
    console.log('A calcular a posição', i);
    return soma + valor;
  }, 0);
}

somar().then(soma => console.log(soma));

1 answer

4

The problem (after half an hour) is that the callback of the .reduce, being asynchronous, returns a Promise... i.e., the first callback argument (after the first iteration) is a Promise.

In order to use the value that is being added up we need to solve Promise

return compras.reduce(async(_soma, tipo, i) => {
  const soma = await _soma;

And for this to work in the first iteration it is necessary to pass Promise.resolve(0) as the initial value (second argument) of .reduce(callback, segundoArgumento).

The schematic solution is:

  return umaArray.reduce(async(<_promise>, valorIterado) => {
    const acumulado = await <_promise>;
    // algo assíncrono;
    return ...
  }, Promise.resolve(<valor inicial>));

And the example at work:

const tipos = ['leite', 'manteiga', 'pão'];
const precos = [23, 21, 32];

const buscarPreço = (tipo) => new Promise((res) => {
  const indice = tipos.indexOf(tipo);
  setTimeout(() => res(precos[indice]), 500);
});

const somar = async() => {
  const compras = ['leite', 'leite', 'manteiga', 'pão', 'pão', 'pão'];
  return compras.reduce(async(_soma, tipo, i) => {
    const soma = await _soma;
    const valor = await buscarPreço(tipo);
    console.log('A calcular a posição', i);
    return soma + valor;
  }, Promise.resolve(0));
}

somar().then(soma => console.log(soma));

Browser other questions tagged

You are not signed in. Login or sign up in order to post.