簡體   English   中英

等待Observable在Observable中完成

[英]Wait for Observable to complete within an Observable

我有以下數據結構:

export class Repo {
    id: number;
    name: string;
    contributors: Contributor[];
}

export class Contributor {
    id: number;
    login: string;
}

我正在使用Observable<Repo>來獲取所有回購數據,但是我想內部調用另一個observable Observable<Contributor>來填充所有貢獻者,然后再將外部Repo視為完全發出。 我不確定該怎么做。 我有以下代碼。

private repos: Repo[] = [];

getRepos(orgName: string): void {
    const repoBuild: Repo[] = [];
    this.githubService.getRepos(orgName).pipe(
        // this here won't wait for all contributors to be resolved
        map(repo => this.getRepoContributors(orgName, repo))
    ).subscribe(
        repo => repoBuild.push(repo),
        error => {
            this.repos = [];
            console.log(error);
        },
        () => this.repos = repoBuild
    );
}

// fetches contributors data for the repo
private getRepoContributors(orgName: string, repo: Repo): Repo {
    const contributors: Contributor[] = [];
    repo.contributors = contributors;
    this.githubService.getRepoContributors(orgName, repo.name)
        .subscribe(
            contributor => {
                // add to the total collection of contributors for this repo
                contributors.push(contributor);
            },
            error => console.log(error),
            () => repo.contributors = contributors
        );
    return repo;
}

我承認我對Observable的理解是有限的,並且我為此苦苦掙扎了好幾個小時。 我試圖在StackOverflow上找到對我有用的東西,但仍然找不到有效的解決方案。 任何幫助,將不勝感激!

(代碼是用Angular 5編寫的)

解:

我在下面使用了@ joh04667建議,最終使它起作用。 這是我的做法:

getRepos(orgName: string): void {
    const repoBuild: Repo[] = [];
    this.githubService.getRepos(orgName).pipe(
        // mergeMap() replaces `repo` with the result of the observable from `getRepoContributors`
        mergeMap(repo => this.getRepoContributors(orgName, repo))
    ).subscribe(
        repo => repoBuild.push(repo),
        error => {
            this.repos = [];
            console.log(error);
        },
        () => this.repos = repoBuild
    );
}

// fetches contributors data for the repo
private getRepoContributors(orgName: string, repo: Repo): Observable<Repo> {
    repo.contributors = []; // make sure each repo has an empty array of contributors
    return this.githubService.getRepoContributors(orgName, repo.name).pipe(
        // tap() allows us to peek on each contributor and add them to the array of contributors
        tap(contributor => {
            // add to the total collection of contributors for this repo
            repo.contributors.push(contributor);
        }),
        // only picks the last contributor and replaces him/her with the repo
        last(
            () => false,
            () => repo,
            repo
        )
    );
}

在我使用last()的最后一部分中,我基本上告訴Observable ,即使它將處理所有值,我也只會消耗最后一個。 最后一個是Contributor類型,但我將其替換為默認值( repo ),這使我可以將返回類型從Observable<Contributor>更改為Observable<Repo> ,這正是我對更高級別的Observable所需要的。

這是一個很好的問題,對我來說,這是真正理解Observable的全部功能的“重大步驟”:高階Observable或返回Observable的Observable。

您的情況對於mergeMap / flatMap運算符是完美的:

getRepos(orgName: string): void {
    const repoBuild: Repo[] = [];
    this.githubService.getRepos(orgName).pipe(
        // this will map the emissions of getRepos to getRepoContributors and return a single flattened Observable
        mergeMap(repo => this.getRepoContributors(orgName, repo))
    ).subscribe(
        repo => repoBuild.push(repo),
        error => {
            this.repos = [];
            console.log(error);
        },
        () => this.repos = repoBuild
    );
}

mergeMap將外部Observable (getRepos)發射映射到內部Observable( getRepoContributors ),並返回僅在內部Observable完成時才發射的新Observable。 換句話說,它將從一個Observable傳遞到另一個Observable數據流的值展平。

高階可觀察物可能很難纏住你的頭,但這確實是可觀察物真正力量所在。 我強烈建議在我鏈接的站點上也檢查其他一些運算符,例如switchMapconcatMap 充分利用可觀察者的力量,他們會變得瘋狂起來。

編輯

我誤讀了原始代碼,以為getRepoContributors返回了一個Observable。 漫長的一天讓我炸了。 我將在這里重構:

map在將值與mergeMap合並之前可以更改其值。 可以在此處完成getRepoContributors的前幾行。 由於mergeMap需要返回一個Observable,因此我們可以簡化一下:

private repos: Repo[] = [];

getRepos(orgName: string): void {
    const repoBuild: Repo[] = [];
    this.githubService.getRepos(orgName).pipe(
        map(repo => {
            repo.contributors = [];
            return repo;
          }),
        mergeMap(repo => this.githubService.getRepoContributors(orgName, repo.name)),
        map(contributor => {
            repo.contributors.push(contributor)
          })
    ).subscribe(
        repo => repoBuild.push(repo),
        error => {
            this.repos = [];
            console.log(error);
        },
        () => this.repos = repoBuild
    );
}

// don't need second function anymore

我們可以在流中map ,以期望值與操作員按順序進行更改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM