4

How to wait for function to finish in JavaScript? I have 2 functions updateSeason and updateFixtures and I want to wait for first one to finish before run next one.

Both my functions are async and they working perfectly fine. Only problem is if I do not use setTimeout I need to run it twice because updateFixture run before updateSeason finish and there is still no file for fucntion to fetch on first run.

updateData

const updateData = async () => {
  await updateSeason();
  await updateFixtures();
};

updateSeason

    // UPDATE SEASON
const updateSeason = async () => {
  // SEASONS TEMPLATE
  let seasonsTemplate = {
    timestamp: new Date(),
    season: null,
    leagues: [],
  };

  // FETCH LEAGUES INFO
  const leagues = await fetch(url + "leagues?subscribed=true&" + key)
    .then((response) => response.json())
    .then((data) => data.data);

  // MAP THROUGH LEAGUES
  leagues.map(async (league) => {
    const id = `${league.league_id}&`;

    // FETCH SEASONS INFO
    const seasons = await fetch(url + "seasons?league_id=" + id + key)
      .then((response) => response.json())
      .then((data) => data.data);

    // MAP THROUGH LEAGUES & POPULATE SEASONS TEMPLATE
    seasons.map((season) => {
      if (season.is_current) {
        seasonsTemplate.season = `${moment(season.start_date).format("YYYY")}_${moment(season.end_date).format("YYYY")}`;
        seasonsTemplate.leagues.push({
          country_id: season.country_id,
          league_id: league.league_id,
          league_name: league.name,
          season_id: season.season_id,
          start_date: season.start_date,
          end_date: season.end_date,
        });
      }
    });

    // CHECK / CREATE SEASON FOLDER
    const currentSeasonFolder = `./data/${seasonsTemplate.season}`;
    if (!existsSync(currentSeasonFolder)) {
      await mkdir(currentSeasonFolder);
      await mkdir(`${currentSeasonFolder}/matches`);
    }

    // CREATE / UPDATE SEASON FILES
    await writeFile("./data/current_season.json", JSON.stringify(seasonsTemplate));
    await writeFile(`${currentSeasonFolder}/season.json`, JSON.stringify(seasonsTemplate));

    console.log(`${league.name} updated...`);
  });
};

updateFixtures

    // UPDATE FIXTURES
    const updateFixtures = async () => {
      // FIXTURES TEMPLATE
      let fixturesTemplate = {
        timestamp: new Date(),
        season: null,
        fixtures: [],
      };
    
      // FETCH CURRENT SEASON INFO
      const season = await fetch(api + "current_season.json").then((response) => response.json());
    
      // POPULATE FIXTURES TEMPLATE SEASON
      fixturesTemplate.season = season.season;
    
      // MAP THROUGH LEAGUES
      season.leagues.map(async (league) => {
        const id = `${league.season_id}&`;
    
        // FETCH COMPETITION FIXTURES
        const fixtures = await fetch(url + "matches?season_id=" + id + key)
          .then((response) => response.json())
          .then((data) => data.data);
    
       

 // MAP THROUGH FIXTURES & POPULATE FIXTURES TEMPLATE
    fixtures.map((match) => {
      if ((match.home_team.team_id === teamId || match.away_team.team_id === teamId) && match.status !== "postponed") {
        fixturesTemplate.fixtures.push({
          match_timestamp: new Date(match.match_start_iso).getTime(),
          match_start: match.match_start_iso,
          match_id: match.match_id,
          status: match.status === "" ? "notstarted" : match.status,
          home_team: getTeamName(match.home_team.team_id),
          home_short: getShortName(match.home_team.team_id),
          away_team: getTeamName(match.away_team.team_id),
          away_short: getShortName(match.away_team.team_id),
        });
      }
    });

    // SORT FIXTURES BY DATE IN ASCENDING ORDER
    fixturesTemplate.fixtures.sort((a, b) => a.match_timestamp - b.match_timestamp);

    // CREATE / UPDATE FIXTURES FILES
    const currentSeasonFolder = `./data/${season.season}`;
    await writeFile(currentSeasonFolder + "/fixtures.json", JSON.stringify(fixturesTemplate));

    console.log("Fixtures updated...");
  });
};

UPDATE:

Issue was inside functions itself. async Array.prototype.map repleaced with for loop in both functions updateSeason and updateFixtures and now is working

Klak031
  • 97
  • 1
  • 1
  • 13
  • 2
    `leagues.map(async (league) =>` this just fires off a bunch of async calls without awaiting them. Use a traditional for...of loop. https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – James Apr 29 '21 at 11:11

4 Answers4

2

You can just use async/await for your defined functions, even if you cannot use 'await' keyword outside an async function.

So, for example, you have a index.js file, you can do:


async function main() {
  await updateSeason();
  await updateFixtures();
}

main()

or invoking directly the function with the short form


(async function main() {
  await updateSeason();
  await updateFixtures();
})()

Anyway, avoid to use 'writeFileSync' or other 'Sync' functions inside an async function because that block the event loop and decrease your performance.

EDIT: I saw now that you are using Array.prototype.map function with an async callback, that could be the problem.

Try to look here: https://flaviocopes.com/javascript-async-await-array-map/

Or, otherwise, use a standard for loop to handle your leagues

Kira
  • 92
  • 3
  • When I use `writeFile` and `mkdir`, functions not working at all, I am getting error then `Callback must be a function. Received undefined` – Klak031 Apr 29 '21 at 10:55
  • Yeah, because since they are async, they need a callback, but you can use fs.promises that work with async/await. Have a look here https://nodejs.org/api/fs.html#fs_promises_api – Kira Apr 29 '21 at 10:59
  • I replaced `writeFileSync` and `mkdirSync` imported from `fs` for `await writeFile` and `await mkdirSync` imported from `fs/promises` but I still getting same outcome FetchError on first run. Code updated – Klak031 Apr 29 '21 at 11:13
  • Sorry, I can't see the update. Anyway, have a look on my edited reponse regarding using map array function. – Kira Apr 29 '21 at 11:19
  • I updated my code and replace `Array.prototype.map` functions with standard for loops and works perfect :) – Klak031 Apr 29 '21 at 12:54
  • I'm happy about that :) – Kira Apr 29 '21 at 13:22
1

Why not just,

async function updateData() {
  await updatedSeason();
  await updateFixtures()
}

updateData();
painotpi
  • 6,894
  • 1
  • 37
  • 70
  • This is what I done originally and for some reason is not working. I think problem is somewhere inside functions itself – Klak031 Apr 29 '21 at 10:53
  • @Klak031 There's an issue with how you're `await`ing inside the `map` function. You're trying to `await` something that's not a promise. I'll try to update my answer with async mocks in a bit. You'll also need to wrap you `map` inside a `Promise.all` – painotpi Apr 29 '21 at 11:15
  • 2
    This is proper way of calling async functions and working perfect. Problem was inside functions with `Array.prototype.map` and `async` which a change to standard `for` loop and now working. – Klak031 Apr 29 '21 at 13:05
0

You forgot to put "await" before the function

const updateData = async () => {
  // Add await before the functions because they are async functions
  await updateSeason()       
  await updateFixtures()
};
Keyboard ninja
  • 725
  • 6
  • 13
  • `await` needs to be used inside an `async` function, unless of course top-level await is what the op has. – painotpi Apr 29 '21 at 10:41
  • This is proper way of calling async functions and working perfect. Problem was inside functions with `Array.prototype.map` and `async` which a change to standard `for` loop and now working. – Klak031 Apr 29 '21 at 13:05
0

Async Await can help you out here since they are both async functions.

const updateData = async () => {
  await updateSeason();
  await updateFixtures();
};
  • 1
    This is proper way of calling async functions and working perfect. Problem was inside functions with `Array.prototype.map` and `async` which a change to standard `for` loop and now working. – Klak031 Apr 29 '21 at 13:05