2

I have a flatMap function that fetch and return data for each item in array. It also returns an empty array if item doesn't have certain informations. Since I'm using flatMap I was expecting that it will remove all the empty arrays, but the finished array still have them included.

Function:

 Promise.all(
        array.flatMap(async (item) => {
        const fetchedData = await fetch(`${item.url}`)
            .then(response => response.json())
            .then(data => data.stats.length ? data : null);
        return fetchedData ? { ...fetchedData } : [];
    })).then(data => console.log(data));

Also when I log the data using console.log(data.flat()) instead of console.log(data) all the empty arrays are removed, but it would require iterating through array one more time. Any ideas why flatMap isn't doing it by itself?

  • `fetchedData ? { ...fetchedData } : []` this looks a bit weird, you're returning either an object or an array depending on `fetchedData`, is that expected? – sp00m Oct 15 '20 at 14:04
  • I think so, that's the syntax of flatMap. Works the same with numbers where you return either a number or empty array and that empty array is being removed. – Adam Ostrowicki Oct 15 '20 at 14:15
  • if you're calling `flat()` on the already `flatMap`ed array you are flattening one more level which would explain the removal of empty arrays – pilchard Oct 15 '20 at 17:33
  • @pilchard yeah but that's what `flatMap` was supposed to do, wasn't it? – Adam Ostrowicki Oct 16 '20 at 18:14
  • Only by one level, per the [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap) `...and then flattening the result by one level. It is identical to a map() followed by a flat() of depth 1, but slightly more efficient than calling those two methods separately.` – pilchard Oct 16 '20 at 21:01

1 Answers1

4

It's because your callback inside of flatMap is an async function, so it always returns a Promise no matter what. Let's say array has two items, the first which will have fetchedData and the second which will not, here's an example of what will happen:

array is: [item1, item2]

After the map part of flatMap you get [Promise(pending), Promise(pending)] (note that the mapping is instantaneous, it doesn't wait for the async functions to complete, it just gets the promise returned from them).

After the flattening part you get [Promise(pending), Promise(pending)] (note that this is the same as the above line - promises can't be flattened.).

Promise.all.then() waits for each promise to resolve and passes the result as data which you log and is [someObject, emptyArray].

JKillian
  • 18,061
  • 8
  • 41
  • 74
  • 1
    is there some way to get this to resolve as expected? How do we get the async to resolve before returning to the flatMap? – WarSame Sep 26 '22 at 18:25