0ca57854e0c91110da2f9e94e301188e.png

与promise转换

Promiserxjs在设计上是解决不同问题的工具,Promise解决的是js中异步回调的问题,rxjs则代表响应式编程,在更高层面提供异步数据流来帮助开发者开发应用。在功能上,rxjs可以通过Observable和operater组合完全达到Promise的效果,将这两者放在一起对比是不太公平的,本文就两者之间的转换联系,已经应用场景进行一些介绍。

observablePromise之间的转换,必须提到observablecomplete状态。所谓complete代表obesrvable的完成状态,在此状态之后,该obserable不会再接受后续的值。

import { Observable } from 'rxjs';
​
const observable = new Observable(function subscribe(subscriber) {
  subscriber.next(1);
  subscriber.next(2);
  subscriber.next(3);
  subscriber.complete();
  subscriber.next(4); // Is not delivered because it would violate the contract
});

结合angular的HttpClient服务get方法等请求得到的都是发射一个值紧接着一个complete通知的observable。源代码:

angular-http-xhr​github.com

这样的observable直接就可以调用toPromise方法得到Promise并能正确resolve得到结果。如果是一个从不completeObservable,则直接调用toPromise方法得到Promise永远都无法resolve。这原因本质上是两个工具的设计不同,如下代码解释。

const promise = new Promise(function (resolve, reject){
  resolve('i am promise');
});
​
const observable = new Observable(function subscribe(subscriber) {
  subscriber.next(1);
  subscriber.next(2);
  subscriber.next(3);
  subscriber.complete();
});
​
const observablePromise = obervable.toPromise();
observablePromise.then((value) => {
  // value应该是1,2还是3?
  // 如果按照调用`then`紧接着调用`subscribe`,那value应该是1,然后是2,然后是3. 但这与then的设计不符
  console.log('observablePromise value', value);
});

如果要对一个未completeObservable转换成promise,你需要指定在当前Observable流信息里摘取你需要有转化成promise的特定信息。

// first 取流中第一个数据,并发射complete信号
const firstPromise = observable.pipe(first());
// 取前三个数据,并进行归并
const threePromise = observable.pipe(
  take(3),
  reduce((acc, val) => acc + val, 0)
);

为什么要进行toPromise转换

其实从设计上看,Observable是比Promise强大非常多的工具,那是否在构建应用的时候使用Observale就可以了。这么说是没错,你完全可以通过只使用Observale来构建应该,比如Angular应用,可以完全不出现Promise。但Promise搭配asyncawait可以在很多情形下增加代码的可读性,维护成本,在一个方法体内,如果需要搭配多个Observable进行组合,使用toPromise搭配await,犹如看到一匹野马在奔跑一样顺畅。

以下代码可以在https://stackblitz.com/edit/rxjs-bmbnpm 进行调试

function pureObservable(conditionA: Observable<boolean>, conditionB: Observable<boolean>, executor: Observable<any>) {
  return conditionA.pipe(
    mergeMap((value) => {
      if (value) {
        return conditionB;
      } else {
        return of(value);
      }
    }),
    mergeMap((value) => {
      if (value) {
        return executor;
      } else {
        return of(false);
      }
    }),
  )
}
​
async function withPromise(conditionA: Observable<boolean>, conditionB: Observable<boolean>, executor: Observable<any>) {
  const predicate = await conditionA.toPromise() && await conditionB.toPromise();
  return predicate && await executor.toPromise();
}

为什么不只使用promise

**以下章节具有比较大的正义,为了Promise而Promise。实际上作者想要实现的是用observable实现能外部触发的promise。和设计,模式啥的都无关。评论者有对promise更本质的思考。

在开发非angular项目时,单纯使用promise搭配async,await存在最大的掣肘是原生ES规范的promise无法从外部调用resolve。例如如下代码,设计一个服务带有一个ready状态

class ServiceA {
  // 状态异步
  readyPromise = Promise.resolve(false);
  data;
  
  async fetchData() {
    const ready = await this.readyPromise;
    if (ready) {
      return this.data;
    } else {
      const data = await this.http.get('/datas');
      this.readyPromise = Promise.resolve(true);
      this.data = data;
      return data;
    }
  }
  
  constructor(private http: HttpService) {
  }
}

看上去这个服务非常健康,获取data的方法能够很好的工作

const sa = new ServiceA();
sa.fetchData().then(...);

但是如果外部获取这个服务的ready状态,则可能出现问题

// 以下异步直接resolve为false,无法监听到ready变为true的是件
const sa = new ServiceA();
sa.readyPromise.then(() => {
  ...
});
​
// 其他方法体内
sa.fetchData().then(...);

求助于stackoverflow最简单的实现方式是

readyResolve;
readyPromise = new Promise((resolve) => this.readyResolve = resolve)
​
// 你想要resolve地方
this.readyResolve(true)

通过rxjs

rxjsSubject类可以从外部触发数据流,只需通过如下简单更改就可以实现ready状态。

class ServiceA {
  private readySubject = new BehaviorSubject<boolean>(false);
  readyPromise = this.readySubject.pipe(
    filter((val) => val),
    first(),
  ).toPromise();
  data;
  
  async fetchData() {
    const ready = this.readySubject.getValue();
    if (ready) {
      return this.data;
    } else {
      const data = await this.http.get('/datas');
      this.readyPromise.next(true);
      this.data = data;
      return data;
    }
  }
  
  constructor(private http: HttpService) {
  }
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐