头图

不废话,直接上结论,jsobservable不能直接使用async/await这种流程控制标识,举个例子

  observable:Observable<any> = new Observable;

  constructor() { 
    this.observable = Observable.create((item:any)=>{//一秒钟后输出结果
      setTimeout(()=>{
        item.next('结果');
        item.complete();
      },1000)
    })
  }

  ngOnInit() {
    console.log(1);
    let startTime = new Date().getTime()
    this.observable.subscribe((res)=>{
      let endTime = new Date().getTime()
      let result = Math.floor(new Date(endTime-startTime).getTime()/1000);
      console.log(res,' 耗时',result,'秒')
    })
    console.log(2)
  }

显而易见,执行结果是
image.png

那么,我想像promise那样使用async/await是否可以呢?
ngOnInite()方法进行稍加改动,加上async/await

  async ngOnInit() {
    console.log(1);
    let startTime = new Date().getTime()
    await this.observable.subscribe((res)=>{
      let endTime = new Date().getTime()
      let result = Math.floor(new Date(endTime-startTime).getTime()/1000);
      console.log(res,' 耗时',result,'秒')
    })
    console.log(2)
  }

啊哈,不好,编辑器提示了
image.png

不信邪,运行看看
image.png

正如上面说的,流程控制并没有起作用,Observable不支持直接的流程控制。

解决办法有两个,首先说一个简单情况下的解决办法
第一种、转为promise 操作如下

 async ngOnInit() {
    console.log(1);
    let startTime = new Date().getTime()
    await this.observable.toPromise().then((res)=>{
      let endTime = new Date().getTime()
      let result = Math.floor(new Date(endTime-startTime).getTime()/1000);
      console.log(res,' 耗时',result,'秒')
    })
    console.log(2)
  }

直接利用.toPromise()方法将其转为promise,这样async/await就起作用了,当然,也要使用promise中的then方法才行。
image.png
可以看到,现在程序会等待promise执行完毕以后才会输出2,也就是流程控制生效了。

注意 有些朋友手动写observable的时候,没有在里面写complete,这样执行toPromise的时候,并不会运行then里面代码,因为promise在底层代码完成之前是不会进行解析的,也就是手写observable中一定要记得写complete

当然,转换成promise并不能解决一切问题,比如下面这种情况

当用户点开页面的时候,需要同时发出50个请求,但是用户很可能在所有数据请求结束之前就会跳走,所以很可能20-30个请求都是无效请求,那么,无效请求就需要直接取消掉

我们知道promise是不能够取消已经发出的请求的,你可以通过throw或者reject触发promisecatch操作,但是这只是代表你不再对发出请求的结果关心,请求依然存在,结果依然会返回。
除非用户在所有请求完成之前点击了浏览器的停止加载按钮,
image.png
那请求就会从pending状态变成了cancel
image.png
image.png

或者,使用subscribe,这也是其对比promise的优势之一
取消subscribe请求很简单:
单个请求的情况下

   this.observable.subscribe((res)=>{}).unsubscribe()

直接在后面加上unsubscribe就行,这个请求会变成cancel状态,不会再占用请求资源

那么, 回到上面的问题,简化一下就是,我可不可以,即能取消已经发出的请求,又能控制代码的流程呢?答案是肯定的,只要把promiseobservable一起搭配使用就行
第二种解决办法、 终极版代码如下

  observableArray:Subscription=new Subscription;
  observable:Observable<any> = new Observable;

  constructor() { 
    this.observable = Observable.create((item:Observer<any>)=>{
      setTimeout(()=>{
        item.next('完成');
        item.complete();
      },1000)
    })
  }

  async ngOnInit() {
    console.log(1);
    let startTime = new Date().getTime()
    await new Promise((resolve,reject)=>{
      this.observableArray.add(this.observable.subscribe((res)=>{
        resolve(true)
        let endTime = new Date().getTime()
        let result = Math.floor(new Date(endTime-startTime).getTime()/1000);
        console.log(res,' 耗时',result,'秒')
      }))
    })
    console.log(2)
  }

  ngOnDestroy(): void {
    this.observableArray.unsubscribe()
  }

运行结果为
image.png

思路就是通过promise控制代码流程,然后利用对象接收Observable,控制请求的取消与否

这样,即可以按照自己的意愿随时取消已经发出的请求,同时,也能控制代码的流程


munergs
30 声望8 粉丝

现在即是最好。