0

I have a nodejs app which fetch data from different api and I want to aggregate the data from different api and then send it out. However I can only resolve the promise of api data fetching but the res.send only sending empty array. I am guessing the res.send didn't wait for the json() function to resolve and already sent out the data. How can I resolve this problem?

app.get('/:keyword',(req,res)=>{
    let unsplashPromise = unsplash.search.photos(......)
    let pixabayPromise = fetch(......)
    let jsonPromise
    let data = []

    Promise.all([unsplashPromise, pixabayPromise]).then(files=>{
        files.map((promise)=>{
            if(promise.url.includes("unsplash")){
                promise.json().then((photos)=>{
                    photos.results.map((photo)=>{
                        data.push({
                            image_ID: photo.id,
                            thumbnails: photo.urls.thumb,
                            preview: photo.urls.regular,
                            title: photo.alt_description,
                            source: "unsplash",
                            tags: photo.tags
                        })
                    })
                })

            }else if(promise.url.includes("pixabay")){
                promise.json().then((json)=>{
                    json.hits.map((photo)=>{
                        data.push({
                            image_ID: photo.id,
                            thumbnails: photo.previewURL,
                            preview: photo.largeImageURL,
                            title: null,
                            source: "pixabay",
                            tags: photo.tags
                        })
                    })
                })
            }
            return data
        })
    }).then(()=>{
        res.send(data)
    })


})
miketsui3a
  • 181
  • 1
  • 4
  • 14

2 Answers2

1

try this, it waits for nested promises.

app.get('/:keyword',(req,res)=>{
    let unsplashPromise = unsplash.search.photos(somthineg)
    let pixabayPromise = fetch(`somthing`)

    let data = []

    Promise.all([unsplashPromise, pixabayPromise]).then(files=>
        Promise.all(files.map((promise)=>{
            if(promise.url.includes("unsplash")){
                return promise.json().then((photos)=>{
                    photos.results.map((photo)=>{
                        data.push({
                            image_ID: photo.id,
                            thumbnails: photo.urls.thumb,
                            preview: photo.urls.regular,
                            title: photo.alt_description,
                            source: "unsplash",
                            tags: photo.tags
                        })
                    })
                })

            }
            if(promise.url.includes("pixabay")){
                return promise.json().then((json)=>{
                    json.hits.map((photo)=>{
                        data.push({
                            image_ID: photo.id,
                            thumbnails: photo.previewURL,
                            preview: photo.largeImageURL,
                            title: null,
                            source: "pixabay",
                            tags: photo.tags
                        })
                    })
                })
            }
            return Promise.resolve();
        }))
    ).then(() => {
        res.send(data);
    }).catch(err=>{
        console.log(err)
    })
})
satanTime
  • 12,631
  • 1
  • 25
  • 73
  • tried but dosen't work – miketsui3a Apr 01 '20 at 10:09
  • in the 1st `then` where you have `promise.json().then`, you need to wait for them too. the easiest way would be to add `async` to your function and then to add `await promise.json().then`. In that case `.then` with `send` will be executed once the data has been collected. – satanTime Apr 01 '20 at 10:16
  • I've updated the code in the answer. – satanTime Apr 01 '20 at 10:20
  • is that a way to catch error from different promise 1 by 1? let say my first api fetch failed but can still get the data of second api fetch – miketsui3a Apr 01 '20 at 11:26
  • you can add a `.catch` before `.then` and to set a flag whether you want to skip the request. when you want to check every promise in `Promise.all` simply add `.catch()` to everyone and map the result to an empty array. then you don't have the error anymore and failed promises will return an empty array. – satanTime Apr 01 '20 at 11:34
  • @ satanTime I am not quite sure what you mean. Did you mean that i can add something like Promise.all([unsplashPromise.catch(err=>{}), pixabayPromise.catch(err=>{})]) ? – miketsui3a Apr 02 '20 at 12:45
  • exactly, you are right :) – satanTime Apr 02 '20 at 15:07
-2

You can add 2 flags, one for unsplash and the other for pixabay and once both are true then you can send the response. e.g.

app.get('/:keyword',(req,res)=>{
let unsplashPromise = unsplash.search.photos(somthineg)
let pixabayPromise = fetch(`somthing`)
let unsplashPromiseDone, pixabayPromiseDone = false;

let data = []

Promise.all([unsplashPromise, pixabayPromise]).then(files=>{
    files.map((promise)=>{
        if(promise.url.includes("unsplash")){
            promise.json().then((photos)=>{
                photos.results.map((photo)=>{
                    data.push({
                        image_ID: photo.id,
                        thumbnails: photo.urls.thumb,
                        preview: photo.urls.regular,
                        title: photo.alt_description,
                        source: "unsplash",
                        tags: photo.tags
                    })
                })
                unsplashPromiseDone = true;
            })

        }else if(promise.url.includes("pixabay")){
            promise.json().then((json)=>{
                json.hits.map((photo)=>{
                    data.push({
                        image_ID: photo.id,
                        thumbnails: photo.previewURL,
                        preview: photo.largeImageURL,
                        title: null,
                        source: "pixabay",
                        tags: photo.tags
                    })
                })
            pixabayPromiseDone = true;
            })
        }
        if (unsplashPromiseDone && pixabayPromiseDone) {
            res.send(data)
        }
    })
}).catch(err=>{
    console.log(err)
})

})

Seema Rawal
  • 247
  • 2
  • 6
  • The `files.map` loop can finish before the inner promises resolve and the flags are set. – Matt Apr 01 '20 at 10:38