rxjs中如何做链序

Posted

技术标签:

【中文标题】rxjs中如何做链序【英文标题】:How to do the chain sequence in rxjs 【发布时间】:2016-10-11 10:07:17 【问题描述】:

我想这样做:

this._myService.doSomething().subscribe(result => 
  doSomething()
);
.then( () => dosthelse() )
.then( () => dosanotherthing() )

所以我想在承诺中链接 .then。我将如何在 Rxjs 中做到这一点?

this._myService.getLoginScreen().subscribe( result => 
      window.location.href = MyService.LOGIN_URL;
      /// I would like to wait for the site to load and alert something from       the url, when I do it here it alerts the old one
    );
   .then (alert(anotherService.partOfTheUrl())


getLoginScreen() 
  return this.http.get(myService.LOGIN_URL)
.flatMap(result => this.changeBrowserUrl())
.subscribe( result => //i want to do sth when the page is loaded//);


changeBrowserUrl(): Observable<any> 
return Observable.create( observer => 
window.location.href = myService.LOGIN_URL;
observer.next();
);

【问题讨论】:

Chaining RxJs Observables in Angular 2的可能重复 注意你的类型!! .subscribe 返回 Disposable 而不是 Observable 【参考方案1】:

对于 observables,then 的等价物是 flatMap。您可以在此处查看一些使用示例:

RxJS Promise Composition (passing data) Why we need to use flatMap? RxJS sequence equvalent to promise.then()?

对于您的示例,您可以执行以下操作:

this._myService.doSomething()
  .flatMap(function(x)return functionReturningObservableOrPromise(x))
  .flatMap(...ad infinitum)
  .subscribe(...final processing)

注意你的函数返回的类型,因为使用flatMap 链接observables 你需要返回一个promise 或一个observable。

【讨论】:

我不知道我会说 flatMap 与 then 是等价的。 flatMap 实际上是对数据进行操作。 reactivex.io/documentation/operators/flatmap.html 如果我们不想要第 n 个 observable 的结果,而是想要没有转换的原始事件怎么办? 有没有办法停止下一个链式方法,以防上一个失败?【参考方案2】:

如果dosthelsedosanotherthing 返回原始值,则要使用的运算符是map。如果它是可观察的,则运算符为 flatMap(或等效项)。

如果你想强制地做某事。我的意思是在异步处理链之外,您可以利用 do 运算符。

假设 dosthelse 返回一个可观察对象,dosanotherthing 返回一个原始对象,您的代码将是:

this._myService.doSomething()
  .do(result => 
    doSomething();
  )
  .flatMap( () => dosthelse() )
  .map( () => dosanotherthing() );

注意,如果你返回订阅方法的返回值,它将对应一个订阅对象,而不是一个可观察对象。订阅对象主要是为了能够取消observable,不能参与异步处理链。

事实上,大多数时候,你是在链的末端订阅的。

所以我会这样重构你的代码:

this._myService.getLoginScreen().subscribe( result => 
  window.location.href = MyService.LOGIN_URL;
  /// I would like to wait for the site to load and alert something from       the url, when I do it here it alerts the old one
  alert(anotherService.partOfTheUrl()
);

getLoginScreen() 
  return this.http.get(myService.LOGIN_URL)
    .flatMap(result => this.changeBrowserUrl())
    .do( result => //i want to do sth when the page is loaded//);


changeBrowserUrl(): Observable<any> 
  return Observable.create( observer => 
    window.location.href = myService.LOGIN_URL;
    observer.next();
  );

【讨论】:

RxJS API 已更改,请参阅此处了解如何使用 Angular 5+ 执行 flatMap:***.com/a/47841977/2506594 从 RxJS v6+ 开始,在 pipe 中使用 tap 运算符而不是 do【参考方案3】:

更新 rxjs 解决方案

自从回答这个问题后,Rxjs 发生了很大变化。

flatMap 现在是 mergeMapswitchMap,它们大多可以互换,但很高兴知道它们的区别 .do() 现在是 tap() 链接现在在.pipe() 内完成。所有操作都应在此管道内完成 如果需要,您可以链接管道(例如,一个变量映射一组用户。另一个变量获取第一个变量并再次映射它)

在最初的调用之后做一些事情

场景

进行 HTTP 调用(例如身份验证检查) 通话结束后,导航到另一个页面
this._myService.getAuthenticated()
  .pipe(
    tap(result => this._myService.navigateToHome())
  )
  .subscribe()

链接多个调用

场景

进行 HTTP 调用(例如身份验证检查) 拨打第二个电话以获取更多信息 两个通话结束后导航
this._myService.getAuthenticated()
  .pipe(
    // The Authentication call returns an object with the User Id
    switchMap(user => this._myService.getUserInfo(user.id))
    // After the user has been loaded, navigate
    tap(user => this._myService.navigateToHome())
  )
  .subscribe()

关于上述示例的注意事项:我假设这些调用是 HTTP,在调用一次后取消订阅。如果您使用实时 observable(例如用户流),请确保您取消订阅或使用 takeUntil/first 运算符。

【讨论】:

以上是关于rxjs中如何做链序的主要内容,如果未能解决你的问题,请参考以下文章

如何理解 RxJS?RxJS的中文API和使用教程

如何在不需要 rxjs-compat 的情况下只导入 RxJS 6 中使用的运算符,如旧的 RxJS?

如何在打字稿中使用 rxjs6 分区?

如何在 Jasmine 中模拟 rxjs webSocket?

如何在 RxJS 订阅方法中等待

如何在 Angular/RxJS 中合并两个 observable?