การเรียกซ้ำด้วย API โดยใช้ Vanilla JS

Aug 18 2020

ฉันกำลังเล่นกับ Rick and Morty API และฉันต้องการรับตัวละครทั้งหมดของจักรวาลลงในอาร์เรย์ดังนั้นฉันจึงไม่ต้องเรียก API เพิ่มเติมเพื่อใช้โค้ดที่เหลือ

จุดสิ้นสุดhttps://rickandmortyapi.com/api/character/ส่งคืนผลลัพธ์ในหน้าดังนั้นฉันต้องใช้การเรียกซ้ำเพื่อรับข้อมูลทั้งหมดในการเรียก API ครั้งเดียว

ฉันสามารถทำให้มันพ่นผลลัพธ์เป็น HTML ได้ แต่ดูเหมือนว่าจะไม่สามารถรับอาร์เรย์ของออบเจ็กต์ JSON ที่สมบูรณ์ได้

ฉันกำลังใช้แนวคิดบางอย่างจากการเรียกซ้ำของ Axios เพื่อแบ่งหน้า API ด้วยเคอร์เซอร์

ฉันแปลแนวคิดสำหรับปัญหาของฉันและฉันได้โพสต์ไว้ในCodepenของฉันนี่คือรหัส:

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}`))

ดวงตาและสมองที่เฉียบคมจะเป็นประโยชน์ มันอาจจะเป็นอะไรที่เรียบง่ายจริงๆและฉันแค่คิดถึงมัน

คำตอบ

3 trincot Aug 18 2020 at 13:34

บรรทัดต่อไปนี้มีปัญหา:

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

ที่นี่คุณจะผลักดันวัตถุสัญญาลงallPeopleและเป็นผลตอบแทนจำนวนคุณจะกลับไปยังหมายเลขที่ไม่.push()allPeople

การใช้forลูปไปยังpushแต่ละรายการจากอาร์เรย์หนึ่งไปยังอีกอาร์เรย์เป็นวิธีการคัดลอกอาร์เรย์โดยละเอียด การวนซ้ำจำเป็นสำหรับส่วน HTML เท่านั้น

นอกจากนี้คุณกำลังผสม.then()ด้วยawaitซึ่งทำให้สิ่งต่างๆซับซ้อน เพียงแค่ใช้awaitเท่านั้น. เมื่อใช้awaitไม่จำเป็นต้องเรียกซ้ำอีกต่อไป เพียงแค่แทนที่ifด้วยลูป:

while (info) {
   ....
   info = res.data.info.next;
}

universePeopleคุณอะไรไม่เคยมอบหมายให้ คุณสามารถวางพารามิเตอร์นี้ได้

แทนที่จะใช้forลูปธรรมดาคุณสามารถใช้for...ofไวยากรณ์ได้

เนื่องจากresคุณใช้เฉพาะdataคุณสมบัติให้ใช้ตัวแปรสำหรับคุณสมบัตินั้นเท่านั้น

ดังนั้นคุณจะได้สิ่งนี้:

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

นี่คือตัวอย่างการทำงานสำหรับฟังก์ชันเรียกซ้ำ

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

ฉันลังเลที่จะเสนอคำตอบอื่นเนื่องจากการวิเคราะห์และคำตอบของ Trincot นั้นตรงประเด็น

แต่ฉันคิดว่าคำตอบซ้ำ ๆ ตรงนี้อาจจะดูหรูหรา และเนื่องจากคำถามถูกแท็กด้วย "การเรียกซ้ำ" ดูเหมือนว่าควรค่าแก่การนำเสนอ

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>

สิ่งนี้เกี่ยวข้องกับการดึงข้อมูลเท่านั้น การเพิ่มลงใน DOM ของคุณควรเป็นขั้นตอนแยกต่างหากและไม่น่าจะยาก

ปรับปรุง: คำอธิบาย

ความคิดเห็นระบุว่าสิ่งนี้ยากที่จะแยกวิเคราะห์ มีสองสิ่งที่ฉันคิดว่าอาจจะยุ่งยากที่นี่:

  • แรกคือdestructuring วัตถุ{info: {next}, results} = <...>ใน นี่เป็นเพียงวิธีที่ดีในการหลีกเลี่ยงการใช้ตัวแปรกลางเพื่อคำนวณตัวแปรที่เราต้องการใช้จริง

  • ประการที่สองคือไวยากรณ์การแพร่กระจายในreturn [...results, ...<more>]. นี้เป็นวิธีที่ง่ายในการสร้างอาร์เรย์กว่าใช้หรือ.concat .push(มีคุณสมบัติคล้ายกันสำหรับวัตถุ)

นี่เป็นอีกเวอร์ชันหนึ่งที่ทำสิ่งเดียวกัน แต่มีตัวแปรระดับกลางและการต่ออาร์เรย์แทน มันทำสิ่งเดียวกัน:

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)
}

ฉันชอบเวอร์ชันดั้งเดิมมากกว่า แต่บางทีคุณอาจพบว่าสิ่งนี้ชัดเจนขึ้น