Rekursion mit einer API unter Verwendung von Vanilla JS
Ich spiele mit der Rick and Morty-API und möchte alle Charaktere des Universums in ein Array integrieren, damit ich keine weiteren API-Aufrufe durchführen muss, um den Rest meines Codes zu bearbeiten.
Der Endpunkt https://rickandmortyapi.com/api/character/gibt die Ergebnisse in Seiten zurück, daher muss ich die Rekursion verwenden, um alle Daten in einem API-Aufruf abzurufen.
Ich kann es dazu bringen, Ergebnisse in HTML auszuspucken, aber ich kann anscheinend kein vollständiges Array von JSON-Objekten erhalten.
Ich verwende einige Ideen aus Axios Rekursion, um eine API mit einem Cursor zu paginieren
Ich habe das Konzept für mein Problem übersetzt und es auf meinem Codepen veröffentlicht. Dies ist der Code:
async function populatePeople(info, universePeople){ // Retrieve the data from the API
let allPeople = []
let check = ''
try {
return await axios.get(info)
.then((res)=>{
// here the current page results is in res.data.results
for (let i=0; i < res.data.results.length; i++){
item.textContent = JSON.stringify(res.data.results[i])
allPeople.push(res.data.results[i])
}
if (res.data.info.next){
check = res.data.info.next
return allPeople.push(populatePeople(res.data.info.next, allPeople))
}
})
} catch (error) {
console.log(`Error: ${error}`) } finally { return allPeople } } populatePeople(allCharacters) .then(data => console.log(`Final data length: ${data.length}`))
Einige scharfe Augen und Gehirne wären hilfreich. Es ist wahrscheinlich etwas wirklich Einfaches und ich vermisse es einfach.
Antworten
Die folgende Zeile hat Probleme:
return allPeople.push(populatePeople(res.data.info.next, allPeople))
Hier schieben Sie ein Versprechen-Objekt hinein allPeople, und wenn Sie .push()eine Nummer zurückgeben, geben Sie eine Nummer zurück, nicht allPeople.
Die Verwendung einer forSchleife für pusheinzelne Elemente von einem Array in ein anderes ist eine ausführliche Methode zum Kopieren eines Arrays. Die Schleife wird nur für den HTML-Teil benötigt.
Außerdem mischen Sie .then()mit await, was die Dinge komplex macht. Nur verwenden await. Bei Verwendung awaitist keine Rekursion mehr erforderlich. Ersetzen Sie einfach die ifdurch eine Schleife:
while (info) {
....
info = res.data.info.next;
}
Sie weisen nie etwas zu universePeople. Sie können diesen Parameter löschen.
Anstelle der einfachen forSchleife können Sie die for...ofSyntax verwenden.
Da resSie nur die dataEigenschaft verwenden, verwenden Sie eine Variable nur für diese Eigenschaft.
Wenn Sie das alles zusammen nehmen, erhalten Sie Folgendes:
async function populatePeople(info) {
let allPeople = [];
try {
while (info) {
let {data} = await axios.get(info);
for (let content of data.results) {
const item = document.createElement('li');
item.textContent = JSON.stringify(content);
denizens.append(item);
}
allPeople.push(...data.results);
info = data.info.next;
}
} catch (error) {
console.log(`Error: ${error}`)
} finally {
section.append(denizens);
return allPeople;
}
}
Hier ist ein Arbeitsbeispiel für eine rekursive Funktion
async function getAllCharectersRecursively(URL,results){
try{
const {data} = await axios.get(URL);
// concat current page results
results =results.concat(data.results)
if(data.info.next){
// if there is next page call recursively
return await getAllCharectersRecursively(data.info.next,results)
}
else{
// at last page there is no next page so return collected results
return results
}
}
catch(e){
console.log(e)
}
}
async function main(){
let results = await getAllCharectersRecursively("https://rickandmortyapi.com/api/character/",[])
console.log(results.length)
}
main()
Ich zögere, eine andere Antwort anzubieten, da Trincots Analyse und Antwort genau richtig sind.
Aber ich denke, eine rekursive Antwort hier kann ziemlich elegant sein. Und da die Frage mit "Rekursion" markiert war, scheint es sich zu lohnen, sie vorzustellen.
const populatePeople = async (url) => {
const {info: {next}, results} = await axios .get (url)
return [...results, ...(next ? await populatePeople (next) : [])]
}
populatePeople ('https://rickandmortyapi.com/api/character/')
// or wrap in an `async` main, or wait for global async...
.then (people => console .log (people .map (p => p .name)))
.catch (console .warn)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script>/* dummy */ const axios = {get: (url) => fetch (url) .then (r => r .json ())} </script>
Dies betrifft nur das Abrufen der Daten. Das Hinzufügen zu Ihrem DOM sollte ein separater Schritt sein und sollte nicht schwierig sein.
Update: Erklärung
Ein Kommentar zeigte an, dass dies schwer zu analysieren ist. Ich stelle mir zwei Dinge vor, die hier schwierig sein könnten:
Erstens ist die Objektzerstörung in
{info: {next}, results} = <...>. Dies ist nur eine gute Möglichkeit, die Verwendung von Zwischenvariablen zu vermeiden, um diejenigen zu berechnen, die wir tatsächlich verwenden möchten.Die zweite ist die Spread-Syntax in
return [...results, ...<more>]. Dies ist eine einfachere Methode zum Erstellen eines Arrays als die Verwendung von.concatoder.push. (Es gibt eine ähnliche Funktion für Objekte.)
Hier ist eine andere Version, die dasselbe tut, jedoch mit einigen Zwischenvariablen und einer Array-Verkettung. Es macht das Gleiche:
const populatePeople = async (url) => {
const response = await axios .get (url)
const next = response .info && response .info .next
const results = response .results || []
const subsequents = next ? await populatePeople (next) : []
return results .concat (subsequents)
}
Ich bevorzuge die Originalversion. Aber vielleicht würden Sie dies klarer finden.