2

So I'm pretty sure this have something to do with Promise.all or something like that but I'm not sure how to do it. will appreciate if someone can help me out with this code.

Note: the array I'm trying to map is an array of objects if that matters

const productsArray = this.cartProducts.map(product => 
      fetch(`/api/products/getDetails/${product.clientId}`))
      .then(res => res.json())
      .then(data => {
        console.log(data)
      })

I'm not able to even launch this code but I'm not sure what I should do exactly...

This is my error from visual studio code.

property 'then' does not exist on type 'promise<response>[]'
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
Ni Tai
  • 499
  • 1
  • 7
  • 13

3 Answers3

3

fetch() result is promise so you need to use Promise.all()

const promises = await Promise.all(this.cartProducts.map(product => fetch(`/api/products/getDetails/${product.clientId}`))
const productsArray = await Promise.all(promises.map(p => p.json()))
wangdev87
  • 8,611
  • 3
  • 8
  • 31
  • thank you this was exactly what i was looking for! it works great however i am getting a 400 stauts error before it works... not sure why – Ni Tai Dec 22 '20 at 13:36
0

William Wang's answer is perfectly good but you may want to use async/await to like this:

const productsArray = this.cartProducts.map(async product => {
      const res = await fetch(`/api/products/getDetails/${product.clientId}`);
      const data = res.json();
      return data;
});

It may be simplified with:

const productsArray = this.cartProducts.map(async product => {
      return await fetch(`/api/products/getDetails/${product.clientId}`).json();
});

However be aware that this kind of pattern will be more "synchronous" than using a Promise.all pattern, as every product will be fetch only after the previous one is fetched. Promise.all will fetch all the product on parallel.

Eve
  • 33
  • 6
  • async await inside map() doesn't work as expected. – wangdev87 Dec 22 '20 at 13:51
  • Can you elaborate ? Is it my snippet ? Or in general ? I've used that pattern a few times and it worked all the way as far as I remember. – Eve Dec 22 '20 at 14:22
0

TL;DR: Map each item in the array to a fetch call and wrap them around Promise.all().

Promise.all(
  this.cartProducts.map(p =>
    fetch(`/api/products/getDetails/${p.clientId}`).then(res => res.json())
  )
).then(products => console.log(products));

Explanation

fetch() returns a Promise a.k.a "I promise I'll give you a value in the future". When the time comes, that future value is resolved and passed to the callback in .then(callback) for further processing. The result of .then() is also a Promise which means they're chainable.

// returns Promise
fetch('...')
// also returns Promise
fetch('...').then(res => res.json())
// also returns Promise where the next resolved value is undefined
fetch('...').then(res => res.json()).then(() => undefined)

So the code below will return another Promise where the resolved value is a parsed Javascript object.

  fetch('...').then(res => res.json())

Array.map() maps each item to the result of the callback and return a new array after all callbacks are executed, in this case an array of Promise.

const promises = this.cartProducts.map(p =>
  fetch("...").then(res => res.json())
);

After calling map, you will have a list of Promise waiting to be resolved. They do not contains the actual product value fetched from the server as explained above. But you don't want promises, you want to get an array of the final resolved values.

That's where Promise.all() comes into play. They are the promise version of the Array.map() where they 'map' each Promise to the resolved value using the Promise API.

Because of that Promise.all() will resolve yet another new Promise after all of the individual promises in the promises array have been resolved.

// assuming the resolved value is Product

// promises is Promise[]
const promises = this.cartProducts.map(p =>
  fetch("...").then(res => res.json())
);

Promise.all(promises).then(products => {
  // products is Product[]. The actual value we need
  console.log(products)
})
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230