Vanilla JS kullanarak bir API ile yineleme

Aug 18 2020

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

3 trincot Aug 18 2020 at 13:34

Aşağıdaki satırda sorunlar var:

return allPeople.push(populatePeople(res.data.info.next, allPeople))

Burada bir promise nesnesini içeri itiyorsunuz allPeopleve .push()bir sayı döndürürken bir sayı döndürüyorsunuz, değil allPeople.

Bir diziden diğerine tek tek öğeler foriç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 awaitişler karmaşık yapıyor, hangi. Sadece kullanın await. Kullanırken awaitartık özyinelemeye gerek yoktur. Sadece ifbir 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 fordöngü yerine for...ofsözdizimini kullanabilirsiniz .

İtibaren ressadece 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;
    }
}
3 UbeytDemir Aug 18 2020 at 13:17

İş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()
2 ScottSauyet Aug 18 2020 at 20:03

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 .concatveya 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.