Search code examples
javascriptarrayspromiseresponseshuffle

How do I fetch a response and then perform array shuffle on it?


I have a block of code here. It fetches 4 API calls to pokiapi, converts the response to JSON, then converts that response to only show the name, and so when Promise.all() resolves it should return an array of 4 Pokemon name.

handleClick(event) {
    event.preventDefault()

    const randNum1 = Math.floor(Math.random() * this.totalPokemon)
    const randNum2 = Math.floor(Math.random() * this.totalPokemon)
    const randNum3 = Math.floor(Math.random() * this.totalPokemon)
    const randNum4 = Math.floor(Math.random() * this.totalPokemon)

    Promise.all([
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum1),
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum2),
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum3),
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum4),
    ])
    .then(responses => Promise.all(responses.map(r => r.json())))
    .then(responses => Promise.all(responses.map(r => r.name)))
    .then(responses => console.log(responses))
}

The last line of code is to console log the response and I get what I expected, which is an array of Pokemon name. An example might be ["mewtwo", "jynx", "ponyta", "geodude"].

BUT, when I include a new line just before console log to shuffle the previous array using fisherYatesShuffle(), the console log now returns undefined:

handleClick(event) {
    event.preventDefault()

    const randNum1 = Math.floor(Math.random() * this.totalPokemon)
    const randNum2 = Math.floor(Math.random() * this.totalPokemon)
    const randNum3 = Math.floor(Math.random() * this.totalPokemon)
    const randNum4 = Math.floor(Math.random() * this.totalPokemon)

    Promise.all([
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum1),
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum2),
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum3),
        fetch('https://pokeapi.co/api/v2/pokemon/' + randNum4),
    ])
    .then(responses => Promise.all(responses.map(r => r.json())))
    .then(responses => Promise.all(responses.map(r => r.name)))
    .then(responses => this.fisherYatesShuffle(responses))
    .then(responses => console.log(responses))
}

I don't think the fisherYatesShuffle() method is wrong because I've tried it on other arrays I make for testing purposes and it was perfectly fine. Just in case, here is the implementation of the fisherYatesShuffle().

//Takes in an array and returns a shuffled array
fisherYatesShuffle(guesses) {
    for (let i = guesses.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * i)
        const temp = guesses[i]
        guesses[i] = guesses[j]
        guesses[j] = temp
    }
}

I suspect that this is a problem with the way the responses are returned, but what can it be?

Edit: Ah apparently the problem WAS my fisherYatesShuffle() method. I originally tested it with some random arrays I made but my testing must've been bad because it didn't catch my error.


Solution

  • The problem is that fisherYatesShuffle doesn't return anything. (There is no return statement in it.) Instead it simply re-orders the array that's passed to it. This means that .then(responses => this.fisherYatesShuffle(responses)) puts undefined as the intermediate result of the Promise chain, so that's what you observe in the console.log.

    There are 2 potential fixes, depending on which part you want to change.

    Option 1 - make fisherYatesShuffle return its contents:

    fisherYatesShuffle(guesses) {
        for (let i = guesses.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * i)
            const temp = guesses[i]
            guesses[i] = guesses[j]
            guesses[j] = temp
        }
        return guesses; // add this line
    }
    

    Option 2 - change the promise chain to explicitly return the shuffled array:

    .then(responses => {
        this.fisherYatesShuffle(responses);
        return responses;
    })
    .then(responses => console.log(responses))