订阅嵌套的可观察对象

subscribing to nested observables

我正在创建一个需要执行几个步骤的可观察对象,每个步骤都依赖于前一个步骤 - 例如获取 ID,使用该 ID 获取用户,使用该用户获取文章.每个步骤都会调用数据库,而数据库又 returns 它自己的可观察对象。我真的不确定如何在不订阅嵌套可观察对象的情况下处理它们,或者确保当订阅外部可观察对象时最终所有嵌套可观察对象也都被订阅。

示例:

newArticle(article: Article): Observable<any> {
   // db api returns an observable of requests
   return this.db.get(`select id from user_ids where name = ${article.name}`).pipe(map((id) => {
      return this.db.get(`select user from users where user_id = ${id}`).pipe(map((user) => {
         return this.db.put(`insert into articles(user_name, title, content) values (${user.name}, ${article.title}, ${article.content});
      }));
   }));
}

使用当前方法不起作用,当从 newArticle 返回的可观察对象被订阅时,似乎只有最外层的可观察对象被订阅和执行。在处理嵌套可观察对象的方式中我遗漏了什么吗?我对 rxjs 比较陌生,很难完全掌握 observables。任何帮助将不胜感激。

在 RxJS 中,避免嵌套订阅的解决方案是使用“Higher Order Mapping Operator" (switchMap, mergeMap, concatMap, exhaustMap”。这些运算符将为您订阅一个“内部可观察对象”并发出它的发射。

我不会在这里深入探讨这些运算符之间的差异,但是 switchMap 会适合您的情况。

您的示例代码非常接近;你基本上可以使用 switchMap 而不是 map.

function newArticle(article: Article) {
    return this.db.get(`select id from user_ids where name = ${article.name}`).pipe(
        switchMap(id => this.db.get(`select user from users where user_id = ${id}`)),
        switchMap(user => this.db.put(`insert into articles(user_name, title, content) values (${user.name}, ${article.title}, ${article.content}`))
    );
}

如果您为特定目的定义单独的函数,您可能会发现代码更容易理解:

function getUserIdForArticle(article: Article): Observable<string> {
    return this.db.get(`select id from user_ids where name = ${article.name}`);
}

function getUser(id: string): Observable<User> {
    return this.db.get(`select user from users where user_id = ${id}`);
}

function storeArticle(user: User, article: Article): Observable<Article> {
    return this.db.put(`insert into articles(user_name, title, content) values (${user.name}, ${article.title}, ${article.content}`);
}
function newArticle(article: Article) {
    return getUserIdForArticle(article).pipe(
        switchMap(id => getUser(id)),
        switchMap(user => storeArticle(user, article))
    );
}