How do I access promiseValue in response array?

Viewed 308

I'm currently having issues when using Promise.all() for the first time (two get-methods). Currently, when I console log my response, I can see the data I'm trying to fetch under promiseValue in the response. But I'm not sure how to access it?

Heres my code:

getData: function () {
            Promise.all([
                fetch('@Url.Action("GetSomething")',
                    {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify(this.selectedValues)
                    }),
                fetch('@Url.Action("GetSomethingElse")',
                    {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify(this.selectedValues)
                    })
            ]).then(responses => {
                if (responses[0].ok && responses[1].ok) {
                    return responses.map(response => {
                        return response.json(); 
                    });
                } else {
                    throw new Error(`Error ${response.status}`);
                }
            }).then(responseArray => {
                console.log("LOG: ", responseArray);

            }).catch(error => {
                console.log(error);
            });
        },

After reading online for answers, there where a few suggestions to add another .then() which gave me the same result (two promises). I looked at related questions asked regarding this issue here on StackOverflow, but only got more confused. I have also tried doing the following which also gave the same results:

...
}).then(responseArray => {

    console.log("LOG: ", responseArray);
    Promise.resolve([responseArray[0], responseArray[1]]).then((results) => {
        console.log("Resolved results: ", results);
    });

}).catch(error => {
    console.log(error);

});

I'm currently stuck! This is what the console.log shows in the response:

LOG: (2) [Promise, Promise]
   >0: Promise: {<Resolved>: (Array(20)}
   >1: Promise: {<Resolved>: (Array(10)}

Help is much appreciated and I'm hoping there is a minor detail I am missing. How can I access these promises? Thanks!

2 Answers

You weren't waiting for the promises that res.json() returns. You could add another Promise.all() on the result of your .map() or you can just bake those into the orginal promise that you use Promise.all() on. The latter is my recommendation:

function fetchJSON(...args) {
    return fetch(...args).then(response => {
        if (!response.ok) {
            throw new Error(`Error ${response.status}`);
        }
        return response.json();
    });
}

getData: function() {
    Promise.all([
        fetchJSON('@Url.Action("GetSomething")', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(this.selectedValues)
        }),
        fetchJSON('@Url.Action("GetSomethingElse")', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(this.selectedValues)
        })
    ]).then(responseArray => {
        console.log("LOG: ", responseArray);
        // use responseArray here
    }).catch(error => {
        console.log(error);
    });
},

FYI, I regularly find that the retrieving of responses using fetch() is unnecessarily verbose and thus end up using some sort of helper function like the fetchJSON() I used here to keep from repeating code.


In my opinion, the fetch() interface should have offered two things.

  1. An option that let you tell it you wanted it to reject if not a 2xx status. I understand the use case for not always rejecting in that case, but there are probably more uses cases where you want it to reject, so it should have been an option.
  2. An option to tell it to read the response and return as text, json or some other type so you don't have to make yet another promise returning function call to do that.

FYI, the request-promise module for nodejs, has both these options (because they are regularly needed).

But, since it doesn't have those options (though they are regularly needed), we sometimes have to wrap it to add those options to avoid repeating code. In this case, you want it to reject if not a 2xx status and you want it to read and parse the JSON and you want to do that in two places, so we make a small utility function that does just that.

Returning a promise in then callback results in resolved promise in next then in a chain.

However, here an array of promises is returned, promises aren't resolved in next then:

return responses.map(response => {
    return response.json(); 
});

There should be either another Promise.all to resolve them in parallel:

return Promise.all(responses.map(response => {
    return response.json(); 
}));

Or it could be rewritten to sequential processing with for loop because no noticeable improvements are expected from parallelism after requests were made.

Any way, async..await makes this more straightforward:

async getData() {
    try {
        const responses = await Promise.all([
            fetch(...),
            fetch(...)
        ]);

        const responseArray = [];
        for (const response of responses) {              
            if (!response.ok)
                throw new Error(`Error ${response.status}`);

            responseArray.push(await response.json());
        }
        ...
    } catch (error){
        console.log(error);
    }
}
Related