如何在JavaScript中等待函数执行完成?

4
如何在JavaScript中等待函数执行完成?
我有两个函数updateSeasonupdateFixtures,我想要在运行下一个函数之前等待第一个函数完成。
我的两个函数都是异步的,并且它们都可以正常工作。唯一的问题是,如果我不使用setTimeout,则需要运行两次,因为updateFixtureupdateSeason完成之前运行,而在第一次运行时还没有用于函数提取的文件。
更新数据
const updateData = async () => {
  await updateSeason();
  await updateFixtures();
};

更新季节
    // 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...`);
  });
};

更新装置
    // 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...");
  });
};

更新:

问题出现在函数内部。在updateSeasonupdateFixtures两个函数中,将async Array.prototype.map替换为for循环,现在已经可以正常工作。


3
leagues.map(async (league) => 这只是触发了一堆异步调用而不等待它们完成。请使用传统的 for...of 循环。https://dev59.com/oFoU5IYBdhLWcg3wV10Z - James
4个回答

2

即使您不能在异步函数之外使用“await”关键字,也可以仅使用async/await来定义函数。

因此,例如,您有一个index.js文件,可以执行以下操作:


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

main()

或者直接使用函数的简短形式调用


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


无论如何,避免在异步函数中使用'writeFileSync'或其他'Sync'函数,因为这会阻塞事件循环并降低性能。
编辑:我现在看到你正在使用带有异步回调的Array.prototype.map函数,可能会出现问题。
请尝试查看此处:https://flaviocopes.com/javascript-async-await-array-map/ 否则,请使用标准for循环来处理您的联赛。

当我使用writeFilemkdir函数时,它们根本不起作用,我会收到错误信息:回调必须是一个函数。接收到未定义的值 - Klak031
是的,因为它们是异步的,所以需要回调函数,但你可以使用支持async/await的fs.promises。在这里看一下:https://nodejs.org/api/fs.html#fs_promises_api - Kira
我已经将从 fs 导入的 writeFileSyncmkdirSync 替换为从 fs/promises 导入的 await writeFileawait mkdirSync,但在第一次运行时仍然出现相同的 FetchError。代码已更新。 - Klak031
抱歉,我看不到更新。不管怎样,请查看我的编辑响应,有关使用映射数组函数的。 - Kira
我更新了我的代码,并用标准的for循环替换了Array.prototype.map函数,现在完美运行 :) - Klak031
我对此感到高兴 :) - Kira

1
为什么不只是这样呢?
async function updateData() {
  await updatedSeason();
  await updateFixtures()
}

updateData();

这是我最初做的,但出了些问题。我认为问题可能在函数内部。 - Klak031
@Klak031,你在map函数内部的await方式有问题。你试图等待一个不是promise的东西。我会尝试稍后更新我的答案,使用异步模拟。你还需要将你的map包装在Promise.all中。 - painotpi
2
这是调用异步函数并完美运行的正确方式。问题在于使用了 Array.prototype.mapasync 的函数,改为标准的 for 循环后现在可以正常工作了。 - Klak031

0

由于它们都是异步函数,因此Async Await可以帮助您解决这个问题。

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

1
这是调用异步函数的正确方式,而且运行完美。问题出在具有 Array.prototype.mapasync 的函数内,通过改为标准的 for 循环现在已经修复。 - Klak031

0

你忘记在函数前面加上 "await" 了

const updateData = async () => {
  // Add await before the functions because they are async functions
  await updateSeason()       
  await updateFixtures()
};

除非顶级await是操作者所需的,否则await需要在异步函数内使用。 - painotpi
这是调用异步函数并正常工作的正确方式。问题出现在使用Array.prototype.mapasync的函数内部,改变为标准的for循环后现在可以工作了。 - Klak031

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接