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

时间:2017-07-07 22:00:27

标签: javascript reactjs asynchronous fetch

使用ReactJS,我有两个不同的API点,我正在尝试获取和重组:Select Left(ColA,3) As NewCol from TableA students。它们都是一组对象。

我的目标是:首先,获得学生和分数,其次,学生和分数保存在州,我将修改它们并根据学生和分数状态创建新状态。简而言之,我有3个功能:scoresgetStudentsgetScores。 <{1}}和rearrangeStudentsAndScores需要在getStudents运行之前完成。

我的问题是:有时getScores会在rearrangeStudentsAndScores完成之前运行。那混乱了rearrangeStudentsAndScores。但有时它会完成。不确定为什么它在50%的时间都有效,但我需要让它在100%的时间内都能正常工作。

这就是我在getScores文件中rearrangeStudentsAndScores 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); } 时,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.students都已加载?

3 个答案:

答案 0 :(得分:28)

您的代码混合了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

答案 1 :(得分:2)

我相信你需要将你的函数包装在箭头函数中。在编译promise链并将其发送到事件循环时,将调用这些函数。这造成了竞争条件。

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

我推荐这篇文章进行额外阅读: We Have a Problem with Promises by Nolan Lawson

这里有一个我做的回购,它为本文中提到的每个概念都有一个例子。 Pinky Swear

答案 2 :(得分:0)

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

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

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