วนซ้ำสัญญาซ้ำ ๆ

ฉันมีอินเทอร์เฟซ REST API ซึ่งทำให้ฉันได้รับข้อมูลบางอย่างระดับแรกเท่านั้น

เช่นฉันต้องการรวบรวมกลุ่ม ทุกกลุ่มสามารถมีกลุ่มย่อยได้ ตัวอย่างเช่น "กลุ่ม 1" มีกลุ่มย่อย "กลุ่ม A" และ "กลุ่ม B" "กลุ่ม A" มีกลุ่มย่อย "GroupX" และอื่นๆ

แต่ API จะให้กลุ่มระดับแรกแก่ฉันสำหรับชื่อกลุ่มเท่านั้น ดังนั้นฉันจึงส่ง "กลุ่ม 1" ไปยัง API และส่งคืน "กลุ่ม A" และ "กลุ่ม B" หากต้องการรับกลุ่มย่อยของกลุ่ม A ฉันต้องเรียก API อีกครั้ง แต่ไม่รู้ว่าจะวนซ้ำอีกกี่ครั้ง

เลยคิดจะใช้การเรียกซ้ำแต่ก็ไม่ได้มาไกล

จนถึงรหัสของฉัน:

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

ตอนนี้จะส่งคืนกลุ่มย่อยแรกขององค์ประกอบแรกให้ฉันเท่านั้น

ฉันจะบรรลุผลสำเร็จได้อย่างไร โดยจะพบทุก Supgroup ไม่ว่าจะกี่กลุ่มก็ตาม อาจจะดีที่จะใช้ Observables?

นี่คือโครงสร้างตัวอย่างของการเรียก API หนึ่งครั้ง:

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

person user5417542    schedule 19.03.2018    source แหล่งที่มา
comment
ข้อมูลของคุณมีลักษณะอย่างไร? คุณจะรู้ได้อย่างไรว่ายังมีระดับอื่นให้ลงลึกอีกหรือไม่?   -  person Bunyamin Coskuner    schedule 19.03.2018
comment
คุณได้ลองใส่ดีบักเกอร์ไว้ใน then callback ของ getGroupChildren แล้วดูว่าจะเกิดอะไรขึ้นหลังจากการวนซ้ำครั้งที่ 1 หรือไม่ มูลค่าที่คุณได้รับ/ส่งต่อสำหรับ data[0].groupName?   -  person palaѕн    schedule 19.03.2018
comment
ซึ่งอาจซ้ำกับ stackoverflow.com/questions/46863275/   -  person Grzegorz Gajos    schedule 19.03.2018
comment
นั่นคือสิ่งที่ผมไม่รู้ว่าจะมีกี่ระดับ ดังนั้นฉันจึงต้องการมันแบบไดนามิก ฉันจะเรียกทรัพยากรทุกกลุ่มและลูกๆ ของกลุ่ม และหากฉันได้รับอาร์เรย์ว่าง หมายความว่าไม่มีกลุ่มย่อยสำหรับกลุ่มนี้อีกต่อไปแล้วฉันก็หยุด   -  person user5417542    schedule 19.03.2018
comment
ไม่น่าเป็นไปได้ที่สิ่งที่สังเกตได้จะนำเสนอสิ่งที่แตกต่างออกไป มันเป็นฟังก์ชันเรียกซ้ำปกติ ฉันขอแนะนำให้ไป async แทนที่จะสัญญาแบบดิบๆ เพื่อทำให้ง่ายขึ้น วิธีแก้ไขที่แท้จริงไม่ชัดเจนเนื่องจากคำถามมีข้อมูลไม่เพียงพอ ฟังก์ชั่นที่ควรส่งคืนคืออะไรกันแน่? เรียกว่า getGroups แต่ส่งคืนสตริงเดียว   -  person Estus Flask    schedule 19.03.2018
comment
ฉันเพิ่มตัวอย่างของการตอบกลับ ฉันส่งคืนวัตถุด้วยอาร์เรย์ข้อมูลของสตริง   -  person user5417542    schedule 19.03.2018
comment
คุณทำสิ่งนี้บนเซิร์ฟเวอร์ไม่ได้เหรอ? การมีส่วนหน้าทำงานหากมีข้อมูลทั้งหมดที่ต้องการไม่ใช่แนวทางปฏิบัติที่มีประสิทธิภาพมากที่สุดเมื่ออาจส่งผลให้เกิดการเรียก REST หลายครั้ง   -  person StudioTime    schedule 19.03.2018
comment
โครงสร้างสุดท้ายที่คุณต้องการได้รับคืออะไร? อาร์เรย์แบบแบนของกลุ่มทั้งหมด มีเพียงใบของแผนผังกลุ่ม หรือโครงสร้างวัตถุที่ซ้อนกันบางส่วน?   -  person trincot    schedule 19.03.2018
comment
เพียงอาร์เรย์ของสตริงของทุกกลุ่ม แต่ฉันคิดว่าถึงแม้ฉันต้องการโครงสร้างอื่น แต่ฉันไม่คิดว่านี่จะเป็นปัญหา ฉันต้องการโทรหา The Promise นาน ๆ จนกว่าฉันจะไม่ได้รับข้อมูลอีกต่อไป   -  person user5417542    schedule 19.03.2018
comment
ตกลง ฉันตอบด้วยโครงสร้างอ็อบเจ็กต์ เนื่องจากอาร์เรย์แบบแบนสามารถถามคำถามเกี่ยวกับลำดับ แล้วจึงรู้ว่าความสัมพันธ์ระหว่างพ่อแม่และลูกคืออะไร โครงสร้างอ็อบเจ็กต์แบบซ้อนมีข้อมูลที่จำเป็นในการสร้างอาร์เรย์แบบแฟลตจากโครงสร้างนั้น   -  person trincot    schedule 19.03.2018


คำตอบ (2)


คุณสามารถบรรลุสิ่งที่คุณต้องการได้ด้วยตัวดำเนินการ flatMap ของ 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);
        }
    });
}

แก้ไข โดยใช้สัญญา

สมมติว่าคุณใช้ GroupService ซึ่งส่งคืนข้อมูลแทนที่จะเป็น HttpClient คุณสามารถแปลง Promise เป็น Observable ด้วยตัวดำเนินการ 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);
        }
    });
}

แก้ไข 2 การใช้บริการนี้

ลองมาดูตัวอย่างของคุณกัน คุณมีการติดตาม json

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

ในไฟล์ส่วนประกอบของคุณ คุณเรียกใช้บริการดังต่อไปนี้

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

แก้ไข 3 รับวัตถุทั้งหมด

คราวนี้เราจะใช้ forkJoin และเรียก getGroups สำหรับรายการลูกทั้งหมดและรวบรวมผลลัพธ์ในอาร์เรย์ children

หมายเหตุ: ฉันยังไม่ได้ทดสอบโค้ดนี้ด้วยตนเอง มันอาจมีข้อผิดพลาดบางอย่าง ถ้ามีก็แจ้งให้ผมทราบด้วย

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
ดูดี. แต่เนื่องจากฉันไม่ได้ทำอะไรกับ Observables มากนัก ฉันจะแปลงสิ่งนั้นได้อย่างไร ฉันใช้ Framework ที่ส่งคืน Promises เท่านั้น ดังนั้นฉันจึงมีคลาสบริการเท่านั้น ซึ่งให้วิธีการประเภท Promise สำหรับการเรียก API แก่ฉัน - person user5417542; 19.03.2018
comment
คุณสามารถใช้ Observable.fromPromise ซึ่งจะแปลง Promise เป็น Observable ได้ตลอดเวลา ที่เหลืออยู่ในคำตอบของฉัน - person Bunyamin Coskuner; 19.03.2018
comment
ตกลง จากนั้นฉันก็สมัครรับข้อมูลในโค้ดของฉัน โดยที่ฉันเรียก getGroups()? ขออภัย แต่ฉันยังใหม่มากกับ Observables - person user5417542; 19.03.2018
comment
ขอบคุณส่วนที่เรียกซ้ำทำงานได้สมบูรณ์แบบ อย่างไรก็ตาม ตอนนี้ฉันยังคงประสบปัญหากับโครงสร้างเอาท์พุตของฉัน ในตัวอย่างของคุณ ฉันจะได้เฉพาะลูกของโหนดสุดท้ายเท่านั้น แต่เนื่องจากฉันต้องการให้ต้นไม้เต็มในตอนท้าย ฉันจึงมองหาวิธีง่ายๆ เพื่อทำซ้ำๆ - person user5417542; 19.03.2018
comment
@ user5417542 ฉันได้อัปเดตคำตอบแล้ว โปรดทดสอบ - person Bunyamin Coskuner; 19.03.2018
comment
ฉันได้รับข้อผิดพลาดที่บรรทัด forkjoin().pipe() มันบอกว่าไม่พบฟังก์ชันแผนที่ - person user5417542; 19.03.2018
comment
คุณimport { map } from 'rxjs/operators';ใช่ไหม? - person Bunyamin Coskuner; 19.03.2018
comment
ขอบคุณครับ คุณอัจฉริยะ :) ทำงานได้สมบูรณ์แบบ ตอนนี้ฉันต้องเข้าใจว่ามันทำงานอย่างไร มีทรัพยากรที่ดีสำหรับสิ่งนี้หรือไม่? - person user5417542; 19.03.2018
comment
ฉันดีใจที่มันใช้งานได้ :) คุณช่วยทำเครื่องหมายว่ายอมรับได้ไหม มีเอกสารมากมายเกี่ยวกับ RxJs คุณควรดูวิธีใช้ตัวดำเนินการ RxJs เช่น flatMap forkJoin และอื่นๆ แผนผังการตัดสินใจนี้อาจช่วยได้เช่นกัน -› reactivex.io/documentation/operators.html#tree - person Bunyamin Coskuner; 19.03.2018

คุณสามารถใช้ Promise.all เพื่อแก้ไขลูกที่ลึกกว่าแบบวนซ้ำ จากนั้นนำผลลัพธ์ (อาร์เรย์) เพื่อสร้างวัตถุเพื่อแก้ไขสัญญาด้วย:

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

ดังนั้นมูลค่าที่สัญญาไว้อาจเป็นดังนี้:

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