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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/44980247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 02:50:09  来源:igfitidea点击:

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

javascriptreactjsasynchronousfetch

提问by Iggy

Using ReactJS, I have two different API points that I am trying to get and restructure: studentsand scores. They are both an array of objects.

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

My goal is: first, get students and scores, and second, with students and scores saved in state, I will modify them and create a new state based on students and scores state. In short, I have 3 functions: getStudents, getScores, and rearrangeStudentsAndScores. getStudentsand getScoresneed to finish before rearrangeStudentsAndScorescan run.

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

My problem is: sometimes rearrangeStudentsAndScoreswill run before getScoreswould complete. That messed rearrangeStudentsAndScoresup. But sometimes it would complete. Not sure why it works 50% of the time, but I need to make it work 100% of the time.

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

This is what I have to fetchstudents and scoresin my Clientfile:

这是我必须fetchstudents 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)
};

I then combined them together:

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

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

In my react app, I have the following:

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

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

Somehow, by the time I get to rearrangeStudentsWithScores, this.state.scoreswill still be [].

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

How can I ensure that this.state.studentsand this.state.scoresare both loaded before I run rearrangeStudentsWithScores?

我如何确保在运行之前都加载了this.state.students和?this.state.scoresrearrangeStudentsWithScores

回答by joews

Your code mixes continuation callbacksand Promises. You'll find it easier to reason about it you use one approach for async flow control. Let's use Promises, because fetchuses them.

您的代码混合了延续回调和承诺。您会发现使用一种异步流控制方法更容易推理。让我们使用 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);
  })

As well as being simpler, this approach is more efficient because it makes both requests at the same time; your approach waited until the students were fetched before fetching the scores.

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

See Promise.allon MDN

在 MDN 上查看Promise.all

回答by illuminatedSpace

I believe you need to wrap your functions in arrow functions. The functions are being invoked as the promise chain is being compiled and sent to the event loop. This is creating a race condition.

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

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

I recommend this article for additional reading: We Have a Problem with Promises by Nolan Lawson

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

And here's a repo I made that has an example for each of the concepts talked about in the article. Pinky Swear

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

回答by Anuj

I would recommend restructuring slightly - instead of updating your state after each fetch call completes, wait for both to complete and then update the state all at once. you can then use the setStatecallback methodto run the next method you would like to.

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

You can use a Promise library such as Bluebirdto wait for multiple fetch requests to finish before doing something else

您可以使用 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);
});