การเรียกซ้ำด้วย API โดยใช้ Vanilla JS
ฉันกำลังเล่นกับ 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}`))
ดวงตาและสมองที่เฉียบคมจะเป็นประโยชน์ มันอาจจะเป็นอะไรที่เรียบง่ายจริงๆและฉันแค่คิดถึงมัน
คำตอบ
บรรทัดต่อไปนี้มีปัญหา:
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;
}
}
นี่คือตัวอย่างการทำงานสำหรับฟังก์ชันเรียกซ้ำ
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()
ฉันลังเลที่จะเสนอคำตอบอื่นเนื่องจากการวิเคราะห์และคำตอบของ 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)
}
ฉันชอบเวอร์ชันดั้งเดิมมากกว่า แต่บางทีคุณอาจพบว่าสิ่งนี้ชัดเจนขึ้น