Problem using map with async and await in Javascript

Asked

Viewed 119 times

3

I have a code block where an array type variable is defined and within a function map, I push some values to that array. Inside the map, I can print the array with the filled objects. However, outside the map the array is empty. I am referring to the variable resultImage.

My code:

async store(req, res) {
  const schema = Yup.object().shape({
    title: Yup.string().required(),
    price: Yup.number().required(),
    negotiable: Yup.boolean().required(),
    status: Yup.boolean(),
    description: Yup.string().required(),

  });

  if (!(await schema.isValid(req.body))) {
    return res.status(400).json({ error: "Validate fails" });
  }

  const { id: idAd, title, price, negotiable, description } = await Ad.create(req.body);

  const imgs = req.files;
  let resultImg = [];

  imgs.map(async item => {
    const { id: idFile, url } = await File.create({ ad_id: idAd, url: item.path });
    resultImg.push({ id: idFile, url });
  })

  console.log(resultImg);

  return res.status(201)
    .json({ idAd, title, price, negotiable, description, imag: { files: resultImg } });
}

1 answer

3


The first thing is not to use map to perform common iterations. The map, as its name says, it is used to map an array in another array. Note that although the map always return a new array, you are not using it.

If you want to perform a "normal" iteration, you can use the Array.prototype.forEach (instead of Array.prototype.map) or some looping loop, like for or for..of. In that case, however, you must use a for normal (or for..of) because the need to callback of methods such as the forEach are not ideal within an asynchronous function.

Therefore:

async function store(req, res) {
  // ...

  const imgs = req.files;
  let resultImg = [];

  for (const item of imgs) {
    const { id: idFile, url } = await File.create({ ad_id: idAd, url: item.path });
    resultImg.push({ id: idFile, url });
  }

  console.log(resultImg); // <-- Estará definido aqui.
}

However, notice a problem with this code: it processes one image after another sequentially. Although this is not always a problem, one way to "parallelize" this is to use the map to create an array of promises and Promise.all to await the resolution (simultaneously) of all of them:

async function store(req, res) {
  // ...

  const imgs = req.files;
  const resultImgPromises = imgs.map(async (item) => {
    const { id: idFile, url } = await File.create({ ad_id: idAd, url: item.path });

    // Note que estamos retornando:
    return { id: idFile, url };
  });

  // Aguardamos que todas as promessas se resolvam.
  // Não esqueça de tratar eventuais erros.
  const resultImg = await Promise.all(resultImgPromises);

  console.log(resultImg); // <-- Estará definido aqui.
}

But, depending on the amount of promises you will be dealing with at the same time, this approach can also bring some problems. A strategy is to create a type of pool, so as to deal with 10 or 15 (or some number you consider feasible) at the same time, dealing with each block "sequentially".

Don’t forget to try to understand how the map works. And about promises.

  • 1

    Very good explanation. I get it. Thank you.

Browser other questions tagged

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