En utilisant ReactJS, j’essaie d’obtenir et de restructurer deux points d’API différents: students
et scores
. Ils sont tous deux un tableau d'objets.
Mon objectif est : premièrement, obtenir les étudiants et les partitions, et deuxièmement, avec les étudiants et les partitions sauvegardées dans un état, je vais les modifier et créer un nouvel état basé sur les élèves et l’état des scores. En bref, j'ai 3 fonctions: getStudents
, getScores
et rearrangeStudentsAndScores
. getStudents
et getScores
doivent être terminés avant que rearrangeStudentsAndScores
puisse s'exécuter.
Mon problème est : parfois rearrangeStudentsAndScores
s'exécutera avant que getScores
ne se termine. Ce foutu rearrangeStudentsAndScores
up. Mais parfois, cela se terminerait. Je ne sais pas pourquoi cela fonctionne 50% du temps, mais je dois le faire fonctionner à 100% du temps.
Voici ce que je dois fetch
students and scores
dans mon fichier 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)
};
Je les ai ensuite combinés ensemble:
function getStudentsAndScores(cbStudent, cbScores, cbStudentsScores){
getStudents(cbStudent).then(getScores(cbScores)).then(cbStudentsScores);
}
Dans mon application de réaction, j'ai les éléments suivants:
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;
...
}
}
D'une manière ou d'une autre, au moment où j'arrive à rearrangeStudentsWithScores
, this.state.scores
sera toujours []
.
Comment puis-je m'assurer que this.state.students
et this.state.scores
sont tous deux chargés avant d'exécuter rearrangeStudentsWithScores
?
Votre code mélange rappel de continuation et Promises. Vous trouverez plus facile de raisonner à ce sujet si vous utilisez une approche pour le contrôle de flux async. Utilisons les promesses, parce que fetch
les utilise.
// 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);
})
En plus d'être plus simple, cette approche est plus efficace car elle effectue les deux demandes en même temps. votre approche a attendu que les étudiants aient été récupérés avant d'aller chercher les partitions.
Voir Promise.all
sur MDN
Je crois que vous devez envelopper vos fonctions dans les fonctions de flèche. Les fonctions sont appelées pendant la compilation de la chaîne de promesses et leur envoi à la boucle d'événements. Cela crée une condition de concurrence.
function getStudentsAndScores(cbStudent, cbScores, cbStudentsScores){
getStudents(cbStudent).then(() => getScores(cbScores)).then(cbStudentsScores);
}
Je recommande cet article pour une lecture supplémentaire: Nous avons un problème avec les promesses de Nolan Lawson
Et voici un repo que j'ai fait qui a un exemple pour chacun des concepts mentionnés dans l'article . Pinky Swear
Je recommanderais de restructurer légèrement - au lieu de mettre à jour votre état après chaque appel d'extraction, attendez que les deux soient terminés, puis mettez à jour l'état en même temps. vous pouvez ensuite utiliser la méthode setState
callback pour exécuter la méthode suivante que vous souhaitez.
Vous pouvez utiliser une bibliothèque Promise telle que Bluebird pour attendre la fin de plusieurs demandes d’extraction avant de faire autre chose.
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);
});