【问题标题】:rxjs observable.pipe(take(1)) vs toPromiserxjs observable.pipe(take(1)) vs toPromise
【发布时间】:2019-10-14 23:00:12
【问题描述】:

最近我被转移到一个新项目,该项目使用 Angular 6 作为前端框架和用于 REST 服务的 spring。

该项目已经开发了 2 年,我观察到几乎所有使用 angular HttpClient 发出的 HTTP 请求都通过管道从 rxjs 获取过滤器。所有 REST API 只发出一个值。不需要手动取消,也不需要 observables 的惰性属性。

我的直觉是使用 toPromise() 会是一种更好的编码方式。

你有什么想法?

  //customer-service.ts
  constructor(private http: HttpClient) {

  }

  public getCustomers() {
     return http.get('/customers');
  }

  //component.ts
  public ngOnInit() {
      this.customerService.getCustomers().pipe(take(1)).subscribe((customers) => {
           //do some stuff
      })
  }

我建议的方法:

  //customer-service.ts
  constructor(private http: HttpClient) {

  }

  public getCustomers() : Promise<Array<Customer>> {
     return http.get('/customers').toPromise();
  }

  //component.ts
  public ngOnInit() {
      this.customerService.getCustomers().then((customers: Array<Customer>) => {
         //do some stuff
      })
  }

我认为我的方法更好,因为它是强类型的并且更干净。

【问题讨论】:

  • 两者都是强类型,Angular 的其余部分使用 Rx,所以没有。
  • 可读性怎么样?你会喜欢.pipe(take(1)).subscribe().then() 吗?计算速度方面的效率如何?
  • 您知道您也可以使用pipe(take(1)) 吗?而且你甚至不必take(1) 因为http observable 在发射后完成。所以它只是subscribethen。但是 observables 更常用也更灵活。
  • 我明白了。还有一个问题:如果“http observable 在发出后完成”我还需要取消订阅吗?
  • 不,你没有。您可以保存订阅并自己与.isUnsubscribed 核对。

标签: javascript angular typescript rxjs observable


【解决方案1】:

实际上,接受的答案是误导性的,说将可观察对象转换为承诺就像倒退了一步。相反,它与您的需求有关。 如Angular Documentation中所述:

Observable 区分链接和订阅。 Promise 只有 .then() 子句。这使得 observables 可用于创建复杂的转换配方以供系统的其他部分使用,而不会导致工作被执行。

toPromise() 和 take(1) (或简称为 operator first())的区别在于 take(1) 在第一个值发出后完成,而 toPromise 等待最后一个发出的值(等待完成的 observable 然后解析发出的最后一个值)。

你可能会感兴趣的 observable 和 promise 之间的另一个区别是 Observable 订阅是可取消的,而 promise 不是。举个具体的例子,假设您的 API /customers 需要时间,但您不再需要结果,然后导航到另一个页面,取消订阅会取消 HTTP 请求。

【讨论】:

    【解决方案2】:

    正如你提到的强类型,我们可以在可观察的方法中做同样的事情。

    
    public getCustomers():Observable<Array<Customer>>  {
         return http.get<Array<Customer>>('/customers');
      }
    
      //component.ts
      public ngOnInit() {
          this.customerService.getCustomers()
          .pipe(take(1))
          .subscribe((customers: Array<Customer>) => {
           //do some stuff
           });
      }
    
    
    • 对于带有单个 HttpResponse 的 HttpRequest,您可以依赖 promise 接近。
    • 但 HttpRequest 具有多个响应,例如 progress/stream 的 data(blob) 采用 Observable 方法。

    尽量使用Observable,熟悉了就不会再选择其他的了。那就试试吧!

    【讨论】:

    • 你甚至不用输入函数的返回类型,它会从http中推断出来。
    • 是的,打字稿会推断,但为了便于阅读,我曾经手动编写。
    • 您可以将鼠标悬停在该方法上,并且您已经在&lt;Array&lt;Customer&gt;&gt; 中明确输入了它,所以我看不出输入两次的原因。
    【解决方案3】:

    我建议继续使用 Observable 方式。

    为什么?因为以后会更容易:

    • 为计划中的功能构建即将推出的逻辑(如映射、过滤等)
    • 使用代码,由于混合使用这两种方法会使其更难读/写

    另一方面,您是对的,您的代码比其他人的代码更具可读性,但您也可以将 Observable 更改为与您的一样好(只需为其添加强类型),并且它仍然保留功能和 Observables 的一致性。

    【讨论】:

      【解决方案4】:

      从 observables 到 Promise 是退一步。

      这就像从保时捷 911 升级到菲亚特 multipla。

      所以不,你不应该使用toPromise(),不,你的方式不是“更好”(那是一些自负的伙伴!)

      我认为我的方法更好,因为它是强类型的并且更干净。

      键入 HTTP 答案不取决于 pormises 或 observables,而是取决于开发人员自己。清洁是一个观点问题,我个人讨厌看到toPromise()s。

      您的解决方案的主要缺点是,一旦转换为 Promise,您将无法再通过管道传输任何内容,从而使您的函数变得不那么通用。

      但他们的代码也不是最好的。通常这种行为用于存储和缓存,你确定你没有遗漏什么吗?

      无论如何,如果不是,我只依赖提供的代码,这将是正确的代码:

      public getCustomers() {
        return http.get<Customer[]>('/customers');
      }
      
      ....
      
      public ngOnInit() {
        this.customerService.getCustomers()
          .subscribe((customers) => {...})
      }
      

      【讨论】:

      • 这种愚蠢的贬义评论。更好的类比是,如果您只需要一辆菲亚特但继续驾驶保时捷,那么当您不使用保时捷的所有功能时,您的维护费用将是您的 10 倍。 @George,请考虑删除您接受的答案。将只触发一个事件的 observable 转换为 Promise 使您的代码更易于遵循和使用,您无需担心处理多次调用的回调,也无需取消订阅。
      猜你喜欢
      • 1970-01-01
      • 2019-11-15
      • 2020-07-25
      • 1970-01-01
      • 1970-01-01
      • 2021-07-06
      • 2020-10-25
      • 2020-11-16
      • 1970-01-01
      相关资源
      最近更新 更多