Mit ReactJS habe ich zwei verschiedene API-Punkte, die ich versuchen zu bekommen und umzustrukturieren: students
und scores
. Sie sind beide ein Array von Objekten.
Mein Ziel ist : erstens Schüler und Ergebnisse bekommen, und zweitens, wenn Schüler und Ergebnisse im Zustand gespeichert sind, werde ich sie ändern und einen neuen Zustand erstellen, der auf dem Schüler- und Ergebniszustand basiert. Kurz gesagt, ich habe 3 Funktionen: getStudents
, getScores
und rearrangeStudentsAndScores
. getStudents
und getScores
müssen beendet werden, bevor rearrangeStudentsAndScores
ausgeführt werden kann.
Mein Problem ist : Manchmal wird rearrangeStudentsAndScores
ausgeführt, bevor getScores
abgeschlossen ist. Das hat rearrangeStudentsAndScores
durcheinander gebracht. Aber manchmal würde es abgeschlossen sein. Ich weiß nicht, warum es 50% der Zeit funktioniert, aber ich muss es 100% der Zeit funktionieren lassen.
Dies ist, was ich fetch
students and scores
in meiner Client
-Datei habe:
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)
};
Ich habe sie dann zusammengefügt:
function getStudentsAndScores(cbStudent, cbScores, cbStudentsScores){
getStudents(cbStudent).then(getScores(cbScores)).then(cbStudentsScores);
}
In meiner Reaktions-App habe ich Folgendes:
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;
...
}
}
Irgendwie, bis ich zu rearrangeStudentsWithScores
komme, wird this.state.scores
noch []
sein.
Wie kann ich sicherstellen, dass this.state.students
und this.state.scores
beide geladen sind, bevor ich rearrangeStudentsWithScores
ausführen kann?
Ihr Code mischt Fortsetzung Rückrufe und Versprechen. Sie finden es einfacher, zu begründen, dass Sie einen Ansatz für die asynchrone Flusskontrolle verwenden. Verwenden wir Versprechen, weil fetch
sie verwendet.
// 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);
})
Dieser Ansatz ist nicht nur einfacher, sondern auch effizienter, da beide Anforderungen gleichzeitig gestellt werden. Ihr Ansatz wartete, bis die Schüler abgeholt wurden, bevor sie die Noten abholten.
Siehe Promise.all
in MDN
Ich glaube, Sie müssen Ihre Funktionen in Pfeilfunktionen einschließen. Die Funktionen werden aufgerufen, während die Versprechenkette kompiliert und an die Ereignisschleife gesendet wird. Dies schafft eine Wettlaufsituation.
function getStudentsAndScores(cbStudent, cbScores, cbStudentsScores){
getStudents(cbStudent).then(() => getScores(cbScores)).then(cbStudentsScores);
}
Ich empfehle diesen Artikel für zusätzliche Lektüre: Wir haben ein Problem mit Versprechen von Nolan Lawson
Und hier ist ein Repo, das ich gemacht habe und ein Beispiel für jedes der Konzepte hat, von denen im Artikel die Rede ist . Pinky Swear
Ich würde die Umstrukturierung etwas empfehlen. Statt Ihren Status nach jedem Abrufaufruf zu aktualisieren, warten Sie, bis beide abgeschlossen sind, und aktualisieren Sie dann den Status auf einmal. Sie können dann die setState
callback-Methode verwenden, um die nächste Methode auszuführen, die Sie möchten.
Sie können eine Promise-Bibliothek wie Bluebird verwenden, um zu warten, dass mehrere Abrufanforderungen abgeschlossen sind, bevor Sie etwas anderes tun
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);
});