繁体   English   中英

Javascript使用promises函数循环遍历数组,以随机顺序返回值

[英]Javascript looping through array with a promises function returning values in random order

我的代码旨在遍历一系列运动队 ID,以使用它们从 API 编译各种信息,然后将 HTML 添加到我的页面以显示每个球队最近完成的比赛。 它正在工作并且看起来很棒,除了:每次刷新时,结果都会以随机顺序出现在我的页面上。 我认为这与循环快速移动到下一次迭代有关,而来自服务器的响应随机返回。 我希望结果首先是“爱国者”,然后是“钢人”等,但结果是随机的,爱国者几乎不会先出现。

另外,我对 JS 很陌生,所以我确信我可以做很多事情来改进它,所以提前谢谢你!

//Define the teams I want scores from and their API reference numbers.
let teams = new Map();
teams.set("Patriots", 134920);
teams.set("Steelers", 134925);
teams.set("Bruins", 134830);
teams.set("Penguins", 134844);
teams.set("Celtics", 134860);
teams.set("Red Sox", 135252);
teams.set("Huskers", 136923);

let teamArr = Array.from(teams.values());
for (i = 0; i < teamArr.length; i++) {
    console.log(teamArr[i]);
}

//Get the team data so that we can pull the logo image.
async function getTeamData(teamID) {
    let result = await fetch(`https://www.thesportsdb.com/api/v1/json/1/lookupteam.php?id=${teamID}`)
    let teamData = await result.json();
    return teamData.teams[0];
}

//Get the info for the teams last game.
async function getLastGame(teamID) {
    let result = await fetch(`https://www.thesportsdb.com/api/v1/json/1/eventslast.php?id=${teamID}`)
    const lastGames = await result.json();
    const lastGame = lastGames.results[0];
    return lastGame;
};

//Populate the final scores with new HTML after pulling all the info from the API.
for (let i = 0; i < teamArr.length; i++) {
    let homeTeam, awayTeam, homeTeamData, awayTeamData, homeTeamLogo, gameDay;
    getLastGame(teamArr[i])
    .then(lastGame => {
        gameDay = lastGame.dateEvent;
        homeTeam = {
            name: lastGame.strHomeTeam,
            id: lastGame.idHomeTeam,
            score: lastGame.intHomeScore,
        };
        awayTeam = {
            name: lastGame.strAwayTeam,
            id: lastGame.idAwayTeam,
            score: lastGame.intAwayScore
        }; //This is all the info we need except for the team icons.
    }).then(result => {
        homeTeamData = getTeamData(homeTeam.id)
        return homeTeamData; 
    }).then(result => {
        homeTeam.logo = result.strTeamBadge;
    }).then(() => {
        awayTeamData = getTeamData(awayTeam.id)
        return awayTeamData;
    }).then(result => {
        awayTeam.logo = result.strTeamBadge; //After getting who was home and who was away, these let us pull and put the right icon in the table.
    }).then(() => {
        let html = `    <tr>
                            <th><img src="%awayImg%" alt="Away" id="${i}-away" class="team-logo"></th>
                            <th><div class="at-vs">@</div></th>
                            <th><img src="%homeImg" alt="Home" id="${i}-home" class="team-logo"></th>
                        </tr>
                        <tr>
                            <th><div class="away-score">%awayScore%</div></th>
                            <th><div class="gameday">%gameDay%</div></th>
                            <th><div class="home-score">%homeScore%</div></th>
                        </tr>`;
        let newhtml = html.replace(`%awayImg%`, awayTeam.logo + "/preview");
        newhtml = newhtml.replace(`%homeImg`, homeTeam.logo + "/preview");
        newhtml = newhtml.replace(`%awayScore%`, awayTeam.score);
        newhtml = newhtml.replace(`%gameDay%`, gameDay);
        newhtml = newhtml.replace(`%homeScore%`, homeTeam.score);

        document.querySelector(`.past-games-table`).insertAdjacentHTML(`beforeend`, newhtml);

    })
};

您正在混合实现相同目标的两种模式。 这是您使用async await重构的代码

//Define the teams I want scores from and their API reference numbers.
let teams = new Map();
teams.set("Patriots", 134920);
teams.set("Steelers", 134925);
teams.set("Bruins", 134830);
teams.set("Penguins", 134844);
teams.set("Celtics", 134860);
teams.set("Red Sox", 135252);
teams.set("Huskers", 136923);

let teamArr = Array.from(teams.values());
for (i = 0; i < teamArr.length; i++) {
    console.log(teamArr[i]);
}

//Get the team data so that we can pull the logo image.
async function getTeamData(teamID) {
    let result = await fetch(`https://www.thesportsdb.com/api/v1/json/1/lookupteam.php?id=${teamID}`)
    let teamData = await result.json();
    return teamData.teams[0];
}

//Get the info for the teams last game.
async function getLastGame(teamID) {
    let result = await fetch(`https://www.thesportsdb.com/api/v1/json/1/eventslast.php?id=${teamID}`)
    const lastGames = await result.json();
    const lastGame = lastGames.results[0];
    return lastGame;
};

//Populate the final scores with new HTML after pulling all the info from the API.
for (let i = 0; i < teamArr.length; i++) {
    let homeTeam, awayTeam, homeTeamData, awayTeamData, homeTeamLogo, gameDay;
    const lastGame = await getLastGame(teamArr[i]);
    gameDay = lastGame.dateEvent;
    homeTeam = {
        name: lastGame.strHomeTeam,
        id: lastGame.idHomeTeam,
        score: lastGame.intHomeScore,
    };
    awayTeam = {
        name: lastGame.strAwayTeam,
        id: lastGame.idAwayTeam,
        score: lastGame.intAwayScore
    }; //This is all the info we need except for the team icons.
    homeTeamData = await getTeamData(homeTeam.id);
    homeTeam.logo = homeTeamData.strTeamBadge;
    awayTeamData = await getTeamData(awayTeam.id);
    awayTeam.logo = awayTeamData.strTeamBadge; //After getting who was home and who was away, these let us pull and put the right icon in the table.
    let html = `    <tr>
                        <th><img src="%awayImg%" alt="Away" id="${i}-away" class="team-logo"></th>
                        <th><div class="at-vs">@</div></th>
                        <th><img src="%homeImg" alt="Home" id="${i}-home" class="team-logo"></th>
                    </tr>
                    <tr>
                        <th><div class="away-score">%awayScore%</div></th>
                        <th><div class="gameday">%gameDay%</div></th>
                        <th><div class="home-score">%homeScore%</div></th>
                    </tr>`;
    let newhtml = html.replace(`%awayImg%`, awayTeam.logo + "/preview");
    newhtml = newhtml.replace(`%homeImg`, homeTeam.logo + "/preview");
    newhtml = newhtml.replace(`%awayScore%`, awayTeam.score);
    newhtml = newhtml.replace(`%gameDay%`, gameDay);
    newhtml = newhtml.replace(`%homeScore%`, homeTeam.score);

    document.querySelector(`.past-games-table`).insertAdjacentHTML(`beforeend`, newhtml);

};

您可以在async函数中做的一件事是await Promise 的解析。 for循环中,这意味着您不会继续执行循环,直到之前的迭代解决为止。 这是一个简单的例子。 如果您将所有提取汇总到一个数组中(例如[getLastGame(teamArr[0]), getLastGame(teamArr[1]), getLastGame(teamArr[2]), etc.] ),那么您可能会得到类似的结果,然后在每个元素上使用await循环遍历该数组。

 const timed = (id) => new Promise(res => { setTimeout(() => res(id), Math.random() * 2000); }) const func = async () => { const promises = [timed(1), timed(2), timed(3), timed(4)]; for (let promise of promises) { const returned = await promise; console.log(returned); } } func();

您可以考虑的其他事情是,再次汇总所有承诺,然后使用Promise.all等待所有承诺的解决,然后在所有数据到达时执行某些操作:

Promise.all([getLastGame(teamArr[0]), getLastGame(teamArr[1], etc.)])
  .then(all => {
    // the `all` variable will contain an array of all resolved promises
  })

似乎您想要的是发出并发请求以获取数据,但无论何时到达,仍以特定顺序显示它们。

现在,您正在一个接一个地提出所有请求,并在获得结果时显示结果。 这意味着结果显示的顺序是不可预测的,任何先前的请求都可以在任何后续请求之前完成,并且您的数据将无序显示。

尝试多次运行此代码段,显示顺序不可预测:

请注意,在下面的示例中,我使用了一些假函数来表示数据请求并显示它。 fakeRequest是一个函数,它可以代表除了最后一个函数之外的所有函数(用于获取和构建团队数据的所有逻辑),而fakeDisplay是一个函数,它可以代表附加 HTML 并显示数据的链的最后一部分.

 const fakeRequest = n => new Promise( resolve => setTimeout(() => { console.log(`requesting ${n}`); resolve(n); }, Math.random() * 1000) ); const fakeDisplay = n => console.log(`displaying ${n}`); // all requests are made one after the other // and as soon as a request is done, we display // the data, no matter which request that is for (let i = 0; i < 3; i++) { fakeRequest(i).then(fakeDisplay); }

这里的主要问题是您的显示逻辑与获取团队数据的逻辑联系在一起。 拆分此行为将允许您以多种方式解决您的问题。

解决此问题的一种方法是在前一个请求+显示操作完成之前不发出请求(并且不显示数据):

 const fn = async () => { // note: using await is only valid in an async function const fakeRequest = n => new Promise( resolve => setTimeout(() => { console.log(`requesting ${n}`); resolve(n); }, Math.random() * 1000) ); const fakeDisplay = n => console.log(`displaying ${n}`); // make each request and wait for it to finish // then display the result and start next request for (let i = 0; i < 3; i++) { const item = await fakeRequest(i); fakeDisplay(item); } } fn();

另一种缓解这种情况的方法是等待所有请求都完成,然后立即显示所有结果(通过使用Promise.all ):

 const fakeRequest = n => new Promise( resolve => setTimeout(() => { console.log(`requesting ${n}`); resolve(n); }, Math.random() * 1000) ); const fakeDisplay = n => console.log(`displaying ${n}`); // make the requests one after the other // and save all the promises in an array const promises = [] for (let i = 0; i < 3; i++) { promises.push(fakeRequest(i)); } // then use a built-in utility Promise.all to // display all the results once they all arrive Promise.all(promises) .then(results => { for (const item of results) { fakeDisplay(item); } })

这样做的主要问题是,在完成所有请求之前,您的 UI 不会显示任何内容,这可能需要一些时间并且可能不是理想的体验。

如果您大约在同一时间提出所有请求,但仍然按正确的顺序显示结果,那么您的 UX 可能会是最好的。

 const fn = async () => { const fakeRequest = n => new Promise( resolve => setTimeout(() => { console.log(`requesting ${n}`); resolve(n); }, Math.random() * 1000) ); const fakeDisplay = n => console.log(`displaying ${n}`); // make the requests one after the other // and save all the promises in an array const promises = [] for (let i = 0; i < 3; i++) { promises.push(fakeRequest(i)); } // loop through the promises and resolve each // one and display the data for await (const item of promises) { fakeDisplay(item); } } fn();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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