Mengulangi Janji secara rekursif

Saya memiliki antarmuka REST API yang hanya memberi saya beberapa informasi tingkat pertama.

Jadi misalnya saya ingin mengumpulkan kelompok. Setiap Grup dapat memiliki subgrup. Jadi misalnya "Grup 1" mempunyai Subgrup "Grup A" dan "Grup B". "Grup A" memiliki Subgrup "GrupX". Dan seterusnya.

Tetapi API hanya memberi saya Grup tingkat pertama untuk nama grup. Jadi saya meneruskan "Grup 1" ke API dan mengembalikan "Grup A" dan "Grup B". Untuk mendapatkan supgrup Grup A, saya perlu memanggil API lagi. Tapi saya tidak tahu berapa banyak iterasi yang akan terjadi.

Jadi saya berpikir untuk menggunakan rekursi tetapi saya belum sampai sejauh ini.

Sejauh ini Kode saya:

getGroupChildren(group:string){ return this restService.getGroupChildren(group)}

getGroups():Promise<any>{
  let collection:string[] = [];
    return this.getGroupChildren("Group A").then((result)=> {
      if(result.data.length !==0){
         return this.getGroupChildren(data[0].groupName);
      }
    });
}

Sekarang ini hanya akan mengembalikan saya Supgrup pertama dari elemen pertama.

Bagaimana saya bisa memastikan bahwa setiap Supgroup akan selalu ditemukan, tidak peduli berapa jumlahnya? Mungkin bagus menggunakan Observables?

Berikut contoh struktur satu panggilan API:

{  "groupName" : "Group_1",  "children" : ["Group_A", "Group_B"]}

person user5417542    schedule 19.03.2018    source sumber
comment
Seperti apa data Anda? Bagaimana Anda tahu jika masih ada tingkatan lain yang harus dipelajari lebih dalam?   -  person Bunyamin Coskuner    schedule 19.03.2018
comment
Sudahkah Anda mencoba memasukkan debugger ke dalam then panggilan balik getGroupChildren dan melihat apa yang terjadi setelah iterasi pertama? nilai apa yang Anda dapatkan/luluskan untuk data[0].groupName?   -  person palaѕн    schedule 19.03.2018
comment
Ini mungkin duplikat dari stackoverflow.com/questions/46863275/   -  person Grzegorz Gajos    schedule 19.03.2018
comment
Itu adalah hal yang saya tidak tahu berapa level yang akan ada. Jadi saya perlu melakukannya secara dinamis. Saya akan memanggil sumber daya untuk setiap Grup dan anak-anaknya dan jika saya mendapatkan array kosong itu berarti tidak ada lagi subgrup untuk grup ini dan saya berhenti.   -  person user5417542    schedule 19.03.2018
comment
Kecil kemungkinan observable akan menawarkan sesuatu yang berbeda. Ini fungsi rekursif reguler. Saya menyarankan untuk menggunakan async daripada janji mentah untuk membuatnya lebih mudah. Solusi sebenarnya tidak jelas karena pertanyaannya tidak berisi data yang cukup. Apa sebenarnya fungsi yang seharusnya dikembalikan? Ini disebut getGroups tetapi mengembalikan satu string.   -  person Estus Flask    schedule 19.03.2018
comment
Saya menambahkan contoh Respon. Saya mengembalikan objek dengan array data string.   -  person user5417542    schedule 19.03.2018
comment
Tidak bisakah kamu melakukan ini di server? Mengerjakan front end jika memiliki semua informasi yang dibutuhkan bukanlah praktik yang paling efisien karena dapat menghasilkan banyak panggilan REST   -  person StudioTime    schedule 19.03.2018
comment
Apa struktur akhir yang ingin Anda dapatkan? Susunan datar semua grup, hanya daun pohon grup, atau struktur objek bersarang?   -  person trincot    schedule 19.03.2018
comment
Hanya serangkaian string, dari semua grup. Namun menurut saya meskipun saya menginginkan struktur lain, menurut saya ini tidak akan menjadi masalah. Saya hanya ingin menelpon Promise sekian lama, sampai saya tidak mendapatkan data lagi.   -  person user5417542    schedule 19.03.2018
comment
Oke, saya menjawab dengan struktur objek, karena array datar dapat memunculkan pertanyaan tentang keteraturan, dan kemudian mengetahui yang mana hubungan orangtua-anak. Struktur objek bersarang memiliki informasi yang dibutuhkan untuk membuat array datar darinya.   -  person trincot    schedule 19.03.2018


Jawaban (2)


Anda dapat mencapai apa yang Anda inginkan dengan flatMap operator Observable

getGroups(group: string) {

    return this.http.get(`/group/{group}`).flatMap(response => {
        if (response.children.length === 0) { // you hit a leaf, stop recursion here
             return Observable.of(response);
        } else { // there are more levels to go deeper
             return this.getGroups(response.children[0].groupName);
        }
    });
}

Edit Menggunakan Promise

Katakanlah Anda menggunakan GroupService yang mengembalikan data, bukan HttpClient. Anda dapat mengonversi Promise menjadi Observable dengan operator fromPromise.

getGroups(group: string) {

    return Observable.fromPromise(this.groupService.get(group)).flatMap(response => {
        if (response.children.length === 0) { // you hit a leaf, stop recursion here
             return Observable.of(response);
        } else { // there are more levels to go deeper
             return this.getGroups(response.children[0].groupName);
        }
    });
}

Edit 2 Menggunakan layanan ini

Mari kita lihat contoh Anda. Anda telah mengikuti json

{
    "groupName": "Group_1", 
    "children" : ["Group_A", "Group_B"]
}

Di file komponen Anda, Anda memanggil layanan sebagai berikut

...
this.recursiveGroupService.getGroups("Group_1")
    .subscribe(response => {
        // at this point response will be `Group_A`
    })

Edit 3 Mendapatkan keseluruhan objek

Kali ini kita akan menggunakan forkJoin dan memanggil getGroups untuk semua anak dan mengumpulkan hasilnya dalam array children.

Catatan: Saya sendiri belum menguji kode ini. Ini mungkin mengandung beberapa kesalahan. Jika ya, beri tahu saya.

import { forkJoin, of } from 'rxjs';
import { map } from 'rxjs/operators';

getGroups(group: string) {
    let retVal;
    return Observable.fromPromise(this.groupService.get(group)).flatMap(response => {
        retVal = {
             groupName: response.groupName
        };
        if (response.children.length === 0) { // you hit a leaf, stop recursion here
             return of(retVal);
        } else { // there are more levels to go deeper
             // this will create list of observable for each child
             const children$ = response.children.map(
                       child => this.getGroups(child)); 
             // forkJoin will execute these observables in parallel
             return forkJoin(children$).pipe(
                  map(results => {
                      // results is an array containing children data
                      retVal.children = results;

                      return retVal;
                  })
             );
         }
    });
}
person Bunyamin Coskuner    schedule 19.03.2018
comment
Terlihat bagus. Tapi karena saya belum berbuat banyak dengan Observables, bagaimana cara mengonversinya? Saya menggunakan Kerangka yang hanya mengembalikan Janji, jadi saya hanya memiliki kelas layanan, yang memberi saya metode tipe Janji untuk panggilan API - person user5417542; 19.03.2018
comment
Anda selalu dapat menggunakan Observable.fromPromise yang akan mengubah Promise menjadi Observable. Sisanya ada di jawaban saya - person Bunyamin Coskuner; 19.03.2018
comment
Oke, lalu saya berlangganan di kode saya, di mana saya memanggil getGroups()? Maaf, tapi saya sangat baru di Observables. - person user5417542; 19.03.2018
comment
Terima kasih, Bagian rekursif berfungsi dengan sempurna. Namun sekarang saya masih menghadapi masalah dengan struktur keluaran saya. Dalam contoh Anda, saya hanya akan mendapatkan anak dari node terakhir. Tapi karena saya ingin pohon lengkap pada akhirnya, saya mencari cara mudah, untuk mengulanginya. - person user5417542; 19.03.2018
comment
@ user5417542 Saya telah memperbarui jawaban saya, silakan uji. - person Bunyamin Coskuner; 19.03.2018
comment
Saya menerima kesalahan pada baris forkjoin().pipe(), katanya tidak dapat menemukan fungsi peta - person user5417542; 19.03.2018
comment
Apakah kamu import { map } from 'rxjs/operators'; ? - person Bunyamin Coskuner; 19.03.2018
comment
Terima kasih, kamu jenius :). Bekerja dengan sempurna. Sekarang saya harus mencari tahu cara kerjanya sebenarnya. Apakah ada sumber daya yang bagus untuk ini? - person user5417542; 19.03.2018
comment
Saya senang ini berhasil :) Bisakah Anda menandainya sebagai diterima? Ada banyak sekali dokumentasi tentang RxJs. Anda harus melihat cara menggunakan operator RxJs seperti flatMap forkJoin dan lainnya. Pohon keputusan ini juga dapat membantu -› reactivex.io/documentation/operators.html#tree - person Bunyamin Coskuner; 19.03.2018

Anda dapat menggunakan Promise.all untuk menyelesaikan turunan yang lebih dalam secara rekursif, lalu mengambil hasilnya (array) untuk membuat objek guna menyelesaikan janji dengan:

getGroups(groupName = "Group A") {
    return this.getGroupChildren(groupName).then((result) =>
        Promise.all(result.data.map( ({groupName}) => this.getGroups(groupName) ))
    ).then(children => ({ groupName, children }));
}

Jadi nilai yang dijanjikan bisa berupa:

[{
    groupName: "Group A",
    children: [{
        groupName: "Group A1",
        children: []
    },  {
        groupName: "Group A2",
        children: []
    }]
}]
person trincot    schedule 19.03.2018