如何在 React 中执行下一个函数之前完成所有获取?

IT技术 javascript reactjs asynchronous fetch
2021-05-22 01:58:53

使用 ReactJS,我有两个不同的 API 点,我试图获取和重构它们:studentsscores. 它们都是一个对象数组。

我的目标是:首先,获取学生和分数,其次,将学生和分数保存在状态中,我将修改它们并根据学生和分数状态创建一个新状态。总之,我有3个功能:getStudentsgetScores,和rearrangeStudentsAndScoresgetStudents并且getScores需要完成rearrangeStudentsAndScores才能运行。

我的问题是:有时rearrangeStudentsAndScores会在getScores完成之前运行那搞砸rearrangeStudentsAndScores了。但有时它会完成。不知道为什么它可以在 50% 的时间内工作,但我需要让它在 100% 的时间内工作。

这是我必须fetch students and scores在我的Client文件中的内容:

function getStudents(cb){
    return fetch(`api/students`, {
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        }
    }).then((response) => response.json())
    .then(cb)
};

function getScores(cb){
    return fetch(`api/scores`, {
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        }
    }).then((response) => response.json())
    .then(cb)
};

然后我将它们组合在一起:

function getStudentsAndScores(cbStudent, cbScores, cbStudentsScores){
    getStudents(cbStudent).then(getScores(cbScores)).then(cbStudentsScores);
}

在我的react应用程序中,我有以下内容:

getStudentsAndScores(){
    Client.getStudentsAndScores(
        (students) => {this.setState({students})},
        (scores) => {this.setState({scores})},
        this.rearrangeStudentsWithScores
    )
}

rearrangeStudentsWithScores(){
    console.log('hello rearrange!')
    console.log('students:')
    console.log(this.state.students);
    console.log('scores:');
    console.log(this.state.scores);        //this returns [] half of the time
    if (this.state.students.length > 0){
        const studentsScores = {};
        const students = this.state.students;
        const scores = this.state.scores;
        ...
    }
}

不知何故,当我到达时rearrangeStudentsWithScoresthis.state.scores仍将是[]

我如何确保在运行之前都加载this.state.studentsthis.state.scoresrearrangeStudentsWithScores

3个回答

您的代码混合了延续回调和Promise。您会发现使用一种异步流控制方法更容易推理。让我们使用 Promises,因为fetch使用它们。

// Refactor getStudents and getScores to return  Promise for their response bodies
function getStudents(){
  return fetch(`api/students`, {
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }
  }).then((response) => response.json())
};

function getScores(){
  return fetch(`api/scores`, {
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }
  }).then((response) => response.json())
};

// Request both students and scores in parallel and return a Promise for both values.
// `Promise.all` returns a new Promise that resolves when all of its arguments resolve.
function getStudentsAndScores(){
  return Promise.all([getStudents(), getScores()])
}

// When this Promise resolves, both values will be available.
getStudentsAndScores()
  .then(([students, scores]) => {
    // both have loaded!
    console.log(students, scores);
  })

除了更简单之外,这种方法更有效,因为它同时发出两个请求;您的方法是等到学生被取走后再取分数。

在 MDN 上查看Promise.all

我相信您需要将函数包装在箭头函数中。这些函数在 promise 链被编译并发送到事件循环时被调用。这是在创造竞争条件。

    function getStudentsAndScores(cbStudent, cbScores, cbStudentsScores){
  getStudents(cbStudent).then(() => getScores(cbScores)).then(cbStudentsScores);
}

我推荐这篇文章作为补充阅读: 我们有诺兰劳森的Promise问题

这是我制作的一个 repo,其中包含文章中讨论的每个概念的示例。 勾小指

我建议稍微重构一下——不要在每次 fetch 调用完成后更新你的状态,而是等待两者都完成,然后一次更新状态。然后您可以使用setState 回调方法运行您想要的下一个方法。

您可以使用 Promise 库(例如Bluebird)在执行其他操作之前等待多个 fetch 请求完成

import Promise from 'bluebird'

getStudents = () => {
  return fetch(`api/students`, {
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }
  }).then(response => response.json());
};

getScores = () => {
  return fetch(`api/scores`, {
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }
  }).then(response => response.json());
};

Promise.join(getStudents(), getScores(), (students, scores) => {
    this.setState({
        students,
        scores
    }, this.rearrangeStudentsWithScores);
});