Rekursi dengan API, menggunakan Vanilla JS
Saya bermain dengan Rick and Morty API dan saya ingin memasukkan semua karakter alam semesta ke dalam array sehingga saya tidak perlu membuat lebih banyak panggilan API untuk mengerjakan sisa kode saya.
Endpoint https://rickandmortyapi.com/api/character/
mengembalikan hasil di halaman, jadi saya harus menggunakan rekursi untuk mendapatkan semua data dalam satu panggilan API.
Saya bisa mendapatkannya untuk memuntahkan hasil ke HTML tetapi saya tidak bisa mendapatkan array lengkap objek JSON.
Saya menggunakan beberapa ide dari rekursi Axios untuk membuat halaman api dengan kursor
Saya menerjemahkan konsep untuk masalah saya, dan saya sudah memasangnya di Codepen saya. Ini kodenya:
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}`))
Beberapa mata dan otak yang tajam akan membantu. Ini mungkin sesuatu yang sangat sederhana dan saya melewatkannya.
Jawaban
Baris berikut bermasalah:
return allPeople.push(populatePeople(res.data.info.next, allPeople))
Di sini Anda memasukkan objek promise ke dalam allPeople
, dan saat .push()
mengembalikan angka, Anda mengembalikan angka, bukan allPeople
.
Menggunakan for
loop ke push
item individual dari satu array ke array lainnya benar-benar merupakan cara verbose untuk menyalin sebuah array. Perulangan hanya diperlukan untuk bagian HTML.
Juga, Anda bercampur .then()
dengan await
, yang membuat segalanya menjadi rumit. Gunakan await
saja. Saat menggunakan await
, tidak perlu rekursi lagi. Cukup ganti if
dengan satu lingkaran:
while (info) {
....
info = res.data.info.next;
}
Anda tidak pernah menetapkan apapun universePeople
. Anda dapat menghapus parameter ini.
Alih-alih for
loop biasa , Anda dapat menggunakan for...of
sintaks.
Karena res
Anda hanya menggunakan data
properti, gunakan variabel untuk properti itu saja.
Jadi dengan menggabungkan semua itu, Anda mendapatkan ini:
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;
}
}
Berikut adalah contoh kerja untuk fungsi rekursif
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()
Saya ragu untuk menawarkan jawaban lain karena analisis dan jawaban Trincot tepat.
Tapi saya pikir jawaban rekursif di sini bisa sangat elegan. Dan karena pertanyaan itu ditandai dengan "rekursi", sepertinya pertanyaan itu layak untuk disajikan.
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>
Ini hanya berkaitan dengan pengambilan data. Menambahkannya ke DOM Anda harus menjadi langkah terpisah, dan seharusnya tidak sulit.
Pembaruan: Penjelasan
Sebuah komentar menunjukkan bahwa ini sulit diurai. Ada dua hal yang menurut saya mungkin rumit di sini:
Pertama adalah destructuring objek di
{info: {next}, results} = <...>
. Ini hanyalah cara yang bagus untuk menghindari penggunaan variabel perantara untuk menghitung variabel yang sebenarnya ingin kita gunakan.Yang kedua adalah sintaks penyebaran dalam
return [...results, ...<more>]
. Ini adalah cara yang lebih sederhana untuk membangun larik daripada menggunakan.concat
atau.push
. (Ada fitur serupa untuk objek.)
Berikut versi lain yang melakukan hal yang sama, tetapi dengan beberapa variabel perantara dan rangkaian array sebagai gantinya. Itu melakukan hal yang sama:
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)
}
Saya lebih suka versi aslinya. Tetapi mungkin Anda akan menemukan yang satu ini lebih jelas.