web-dev-qa-db-ja.com

Reactで次の関数を実行する前にすべてのフェッチを終了する方法は?

ReactJSを使用して、studentsscoresの2つの異なるAPIポイントを取得して再構築しようとしています。どちらもオブジェクトの配列です。

私の目標は:最初に、生徒とスコアを取得し、次に、生徒とスコアを状態に保存して、それらを変更し、生徒とスコアの状態に基づいて新しい状態を作成します。つまり、getStudentsgetScores、およびrearrangeStudentsAndScoresの3つの関数があります。 getStudentsgetScoresは、rearrangeStudentsAndScoresを実行する前に終了する必要があります。

私の問題はrearrangeStudentsAndScoresが完了する前にgetScoresが実行されることがあります。 rearrangeStudentsAndScoresを台無しにしました。しかし、時にはそれは完了するでしょう。なぜ50%の頻度で機能するのかはわかりませんが、100%の頻度で機能させる必要があります。

これは私がfetchstudents and scoresClientファイル内:

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

どういうわけか、rearrangeStudentsWithScoresthis.state.scoresは引き続き[]

this.state.studentsおよびthis.state.scoresを実行する前に両方ともロードされますrearrangeStudentsWithScores

19
Iggy

コードは 継続コールバック とPromisesを組み合わせています。非同期フロー制御に1つのアプローチを使用すると、それについて簡単に推論できます。 fetchがPromiseを使用するため、Promiseを使用しましょう。

// 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

36
joews

関数を矢印関数でラップする必要があると思います。 promiseチェーンがコンパイルされ、イベントループに送信されるときに、関数が呼び出されます。これは競合状態を作り出しています。

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

さらに読むためにこの記事をお勧めします: Nolan Lawsonによる約束の問題があります

そして、ここで私が作成したレポは、記事で説明した各概念の例を持っています。 ピンキースウェア

2

フェッチの呼び出しが完了するたびに状態を更新するのではなく、両方を完了するのを待ってから、一度に状態を更新することをお勧めします。次に、setStatecallback 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);
});
0
Anuj