简体   繁体   中英

How can I combine these two async functions?

I'm looping thru a teams array getting team data and players for each team...

I have 2 async functions that pull the same data but I can't figure out how to combine them.

As it works currently, each loop adds the teamPlayers array to a players array in the first async function. On completion I have 1450 player objects in my players array. The second async function adds the team object to a teams array. On completion I have 32 team objects in my teams array.

I've tried changing the first async function to get data and then have the dispatch return a value of results.teamPlayers but I end up with a players array with 32 arrays each with x number of player objects.

async function loadRosters(endPoints, teams, authKey) {
    axios.defaults.headers.common['Authorization'] = authKey;

    const requests = teams.map(teamId => {
        try {
            return axios.get(endPoints.roster + teamId).then(r => r.data.teamPlayers);
        } catch (e) {
            console.log(e);
        }
        return [];
    });

    const results = (await Promise.all(requests)).flat();
    dispatch({ type: 'SET_PLAYERS', value: results });

    return results;
}


async function loadTeamData(endPoints, teams, authKey) {
    axios.defaults.headers.common['Authorization'] = authKey;

    const requests = teams.map(teamId => {
        try {
            return axios.get(endPoints.roster + teamId).then(r => r.data.team);
        } catch (e) {
            //
        }
        return [];
    });

    const results = (await Promise.all(requests)).flat();
    dispatch({ type: 'SET_TEAM_LOGOS', value: results });

    return results;
}

Here is the structure of the data I'm getting:

{
  "team": {
    "teamId": "string",
    "abbr": "string",
    "logo": "string"
  },
  "teamPlayers": [
    {
      "firstName": "string",
      "lastName": "string",
      "esbId": "string",
      "position": "string",
      "jerseyNumber": 0,
      "teamAbbr": "string",
      "headshot": "string"
    }
  ]
}

It may be simpler if I get the team.logo and add it to each player in teamPlayers.

Just as a suggestion, why don't you create an endpoint that accepts multiple team IDs so you don't need to send multiple requests to the server, it can return all the data at once in an array? Also, having both a team and a teamPlayers key returning from that endpoint is slightly dubious design, at least semantically. Anyway, here's my best attempt:

async function loadTeams(endPoints, teams, authKey) {
    axios.defaults.headers.common['Authorization'] = authKey;

    const requests = teams.map(teamId =>
      axios.get(endPoints.roster + teamId).then(res => res.data, e => {
        console.error(e);
        return { teamPlayers: [], team: [] };
      })
    );

    const allTeamData = await Promise.all(requests);
    // Following variables will aggregate ALL players from ALL teams and
    // ALL logos (? it seems your logos are arrays, but that's a bit weird)
    const allTeamPlayers = [], allTeamLogos = [];

    for (const { teamPlayers, team } of allTeamData) {
      allTeamPlayers.push(...teamPlayers); // spread optional
      allTeamLogos.push(...team); // again, spread optional
    }

    dispatch({ type: 'SET_PLAYERS', value: allTeamPlayers });
    dispatch({ type: 'SET_TEAM_LOGOS', value: allTeamLogos });

    return { teamPlayers: allTeamPlayers, team: allTeamLogos };
}

Now you can call the function and get the teamPlayers or team keys of the result to get whatever you need.

To avoid fetching the same team multiple times, you could cache your results. For example, if this is a redux store, you could dispatch teams and place them in your redux state. To put it all together, you can write an async function that either retrieves teams from the store or fetches if it hasn't yet been fetched.

// Not sure how you are getting "dispatch" earlier, but assuming you've got a getState as well...

const inProgress = new Map();

async function loadTeam(endPoints, teamId, authKey) {
  const { teams } = getState();
  if (teamId in teams) {
    return teams[teamId];
  }

  let promise = inProgress.get(teamId);
  if (!promise) {
    axios.defaults.headers.common['Authorization'] = authKey;
    promise = axios.get(endPoints.roster + teamId);
    inProgress.set(teamId, promise);
  }

  const { data: team } = await promise;
  inProgress.delete(teamId);

  // TODO: add a reducer that puts this team in your state
  dispatch({ type: 'SET_TEAM', payload: { teamId, team } });
  return team;
}

Now you don't have to worry about duplicate fetches if you use this loadTeam function:

async function loadRosters(endPoints, teams, authKey) {
    axios.defaults.headers.common['Authorization'] = authKey;

    const requests = teams.map(teamId => loadTeam(endPoints, teamId, authKey));

    const results = (await Promise.all(requests))
      .map(team => team.teamPlayers)
      .flat();
    dispatch({ type: 'SET_PLAYERS', value: results });

    return results;
}

I figured it out but please tell me if you think there is a more efficient solution.

async function loadRosters(endPoints, teams, authKey) {
    axios.defaults.headers.common['Authorization'] = authKey;

    const requests = teams.map(teamId => {
        try {
            return axios.get(endPoints.roster + teamId).then(r => r.data);
        } catch (e) {
            //
        }
        return [];
    });

    const results = (await Promise.all(requests)).flat();
    dispatch({ type: 'SET_PLAYERS', value: results.map(r => r.teamPlayers).flat(Infinity) });
    dispatch({ type: 'SET_TEAM_LOGOS', value: results.map(r => r.team) });

    return results;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM