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

How to finish all fetch before executing next function in React?

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

我的目标是:首先,获取学生和分数,其次,将学生和分数保存在状态中,我将修改它们并创建一个基于学生和分数的新状态分数状态。简而言之,我有 3 个函数:getStudentsgetScoresrearrangeStudentsAndScoresgetStudentsgetScores 需要在 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;
        ...
    }
}

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

如何确保 this.state.studentsthis.state.scores 在 运行 rearrangeStudentsWithScores 之前都已加载?

您的代码混合了 continuation callbacks 和 Promises。您会发现使用一种方法进行异步流控制更容易推理。让我们使用 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);
  })

这种方法不仅更简单,而且效率更高,因为它同时发出两个请求;你的方法是等到学生被取走后再取分数。

Promise.all on MDN

我会建议稍微重组 - 不要在每次提取调用完成后更新您的状态,而是等待两者都完成,然后一次更新所有状态。然后你可以使用 setState callback method 到 运行 你想要的下一个方法。

您可以使用 Bluebird 等 Promise 库来等待多个获取请求完成,然后再执行其他操作

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);
});

我认为您需要将函数包装在箭头函数中。这些函数在编译承诺链并发送到事件循环时被调用。这会造成竞争条件。

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

我推荐这篇文章作为补充阅读: We Have a Problem with Promises by Nolan Lawson

这是我创建的一个存储库,其中包含文章中讨论的每个概念的示例。 Pinky Swear