Vanilla JS kullanarak bir API ile yineleme
Rick and Morty API ile oynuyorum ve evrenin tüm karakterlerini bir diziye dahil etmek istiyorum, böylece kodumun geri kalanını çalıştırmak için daha fazla API çağrısı yapmak zorunda kalmayacağım.
Uç nokta https://rickandmortyapi.com/api/character/
sonuçları sayfalar halinde döndürüyor, bu yüzden tüm verileri tek bir API çağrısında almak için özyinelemeyi kullanmam gerekiyor.
Sonuçları HTML'ye aktarmasını sağlayabilirim, ancak eksiksiz bir JSON nesneleri dizisi elde edemiyorum.
Bir api'yi bir imleçle sayfalandırmak için Axios özyinelemesinden bazı fikirler kullanıyorum
Problemimin konseptini tercüme ettim ve onu Codepen'ime gönderdim Bu kod:
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}`))
Bazı keskin gözler ve beyinler yardımcı olabilir. Muhtemelen gerçekten basit bir şey ve ben onu özlüyorum.
Yanıtlar
Aşağıdaki satırda sorunlar var:
return allPeople.push(populatePeople(res.data.info.next, allPeople))
Burada bir promise nesnesini içeri itiyorsunuz allPeople
ve .push()
bir sayı döndürürken bir sayı döndürüyorsunuz, değil allPeople
.
Bir diziden diğerine tek tek öğeler for
için bir döngü kullanmak push
, bir diziyi kopyalamanın gerçekten ayrıntılı bir yoludur. Döngü yalnızca HTML bölümü için gereklidir.
Ayrıca, karıştırma .then()
ile await
işler karmaşık yapıyor, hangi. Sadece kullanın await
. Kullanırken await
artık özyinelemeye gerek yoktur. Sadece if
bir döngü ile değiştirin :
while (info) {
....
info = res.data.info.next;
}
Asla bir şey atamazsın universePeople
. Bu parametreyi bırakabilirsiniz.
Düz for
döngü yerine for...of
sözdizimini kullanabilirsiniz .
İtibaren res
sadece kullanmak data
özelliğini, o mülke yalnızca bir değişken kullanın.
Tüm bunları bir araya getirerek şunu anlıyorsunuz:
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;
}
}
İşte özyinelemeli fonksiyon için çalışma örneği
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()
Başka bir cevap vermekten çekiniyorum çünkü Trincot'un analizi ve cevabı yerinde.
Ancak burada yinelemeli bir yanıtın oldukça zarif olabileceğini düşünüyorum. Ve soru "özyineleme" ile etiketlendiğinden, sunulmaya değer görünüyor.
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>
Bu sadece verilerin getirilmesi ile ilgilidir. Bunu DOM'nuza eklemek ayrı bir adım olmalı ve zor olmamalı.
Güncelleme: Açıklama
Bir yorum, bunun ayrıştırılmasının zor olduğunu gösterdi. Burada yanıltıcı olabileceğini düşündüğüm iki şey var:
İlk olduğu nesne strüktür içinde
{info: {next}, results} = <...>
. Bu, gerçekten kullanmak istediklerimizi hesaplamak için ara değişkenleri kullanmaktan kaçınmanın güzel bir yoludur.İkinci yayılma sözdizimi içinde
return [...results, ...<more>]
. Bu, bir dizi oluşturmanın.concat
veya kullanmaktan daha basit bir yoludur.push
. (Nesneler için benzer bir özellik vardır.)
İşte aynı şeyi yapan başka bir sürüm, ancak bunun yerine bazı ara değişkenler ve bir dizi birleştirme ile. Aynı şeyi yapar:
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)
}
Orijinal versiyonu tercih ederim. Ama belki bunu daha açık bulursunuz.