如何在链接的 observables 之间传递结果 [英] How to pass results between chained observables

查看:19
本文介绍了如何在链接的 observables 之间传递结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

抽象问题: 每次源 Observable 发出和事件时,都需要触发一系列 API 调用和 Angular 服务.其中一些调用取决于之前的结果.

Abstract problem: Every time a source Observable emits and event, a sequence of API calls and Angular services need to be triggered. Some of those invocations are depending on previous results.

在我的示例中,源 Observable startUpload$ 触发了一系列依赖调用.

In my example, the source Observable startUpload$ triggers a series of depending invocations.

使用解构可以这样写:

this.startUploadEvent$.pipe(
      concatMap(event => this.getAuthenticationHeaders(event)),
      map(({ event, headers }) => this.generateUploadId(event, headers)),
      tap(({ event, headers, id }) => this.emitUploadStartEvent(id, event)),
      concatMap(({ event, headers, id }) => this.createPdfDocument(event, headers, id)),
      concatMap(({ event, headers, id, pdfId }) => this.uploadBilderForPdf(event, pdfId, headers, id)),
      mergeMap(({ event, headers, id, pdfId, cloudId }) => this.closePdf(cloudId, event, headers, id, pdfId)),
      tap(({ event, headers, id, pdfId, cloudId }) => this.emitUploadDoneEvent(id, event, cloudId)),
).subscribe()

它几乎读起来像是一种命令式方法.但它有一些问题:

It almost reads like an imperative approach. But it has certain problems:

  • 解构链在代码上重复并变得越来越长{ event, headers, id, pdfId, cloudId }
  • 方法(如generateUploadId(event, headers))需要接收所有先前的值,以便它们能够将它们传递到下一个管道,即使方法本身不需要它
  • 需要内部 Observables(在方法中)来映射值,以便进一步的管道阶段可以破坏它们:
  • The destructuring chain is repeated over the code and gets longer and longer { event, headers, id, pdfId, cloudId }
  • Methods (like generateUploadId(event, headers)) are required to receive all previous values so that they are able to pass them to the next pipe, even if the method itself doesn't require it
  • Inner Observables (within the methods) are required to map the values so that further pipe stages can destruct them:

_

private closePdf(cloudId, event, headers, id, pdfId) {
    return this.httpClient.post(..., { headers } )
        .pipe(
             //...,
             map(() => ({ event, headers, id, pdfId, cloudId }))
        )
}

如果编译器能够处理样板文件(如async await)来编写这样读取的代码(没有上述问题),那就太好了:

It would be nice if the compiler could take care of the boilerplate (like with async await) to write the code that reads like this (with none of the problems mentioned above):

private startUpload(event: StartUploadEvent) {
    const headers = this.getAuthenticationHeaders(event)
    const id = this.generateUploadId()

    this.emitUploadStartEvent(id, event)

    const pdfId = this.createPdfDocument(event, headers, id)
    this.uploadBilderForPdf(event, pdfId, headers, id)

    const cloudId = this.closePdf(headers, pdfId)
    this.emitUploadDoneEvent(id, event, cloudId)

    return cloudId
  }

如何在没有我提到的问题的情况下在链接的 observables 之间传递结果?有没有我错过的 rxjs 概念?

How to pass results between chained observables without the problems i've mentioned? Is there a rxjs concept i've missed?

推荐答案

你当然不应该让你的方法接受与它们无关的参数!

You certainly shouldn't have your methods take in params that don't concern them!

对于您的主要问题:

如何在没有我提到的问题的情况下在链接的 observables 之间传递结果?

How to pass results between chained observables without the problems i've mentioned?

使用单一作用域(嵌套管道)

下面的代码等同于您的示例代码,无需传递不必要的属性.先前返回的值可以通过函数调用链访问:

Use a single scope (nested pipes)

The code below is equivalent to your sample code, with no need to pass the unnecessary properties. The previously returned values are accessible by function calls further down the chain:

1   startUploadEvent$.pipe(
2     concatMap(event => getAuthenticationHeaders(event).pipe(
3       map(headers => generateUploadId(event, headers).pipe(
4         tap(id => emitUploadStartEvent(id, event)),
5         concatMap(id => createPdfDocument(event, headers, id)),
6         concatMap(pdfId => uploadBilderForPdf(event, pdfId)),
7         tap(cloudId => closePdf(cloudId, event))
8       ))
9     ))
10  ).subscribe();

注意如何在下游访问 eventheaders.它们不需要传递到不需要它们的函数中.

Notice how event and headers are accessible downstream. They do not need to be passed into functions that don't require them.

有没有我遗漏的 rxjs 概念?

Is there a rxjs concept i've missed?

也许吧.?不是真的... :-)

Maybe.? Not really... :-)

诀窍是添加一个 .pipe 来有效地对操作符进行分组,这样他们都可以访问输入参数.

The trick is to tack on a .pipe to effectively group operators so they all have access to the input params.

通常,我们尝试将代码平放在 .pipe 中:

Usually, we try to keep the code flat inside the .pipe:

1   const greeting$ = userId$.pipe(
2     switchMap(id => http.get(`/users/${id}`)),
3     map(response => response.data.userName),
4     map(name => `Hello ${name}!`),
5     tap(greeting => console.log(greeting))
6   );

但该代码实际上与以下内容没有什么不同:

but that code is really no different than:

1   const greeting$ = userId$.pipe(
2     switchMap(id => http.get(`/users/${id}`).pipe(
3       map(response => response.data.userName),
4       map(name => `Hello ${name}! (aka User #${id})`)
5     )),
6     tap(greeting => console.log(greeting))
7   );

但是,在第二种情况下,第 4 行可以访问 nameid,而在第一种情况下,它只能访问 name.

But, in the second case, line #4 has access to the name and the id, whereas in the first case it only has access to name.

注意第一个的签名是userId$.pipe(switchMap(), map(), map(), tap())

第二个是:userId$.pipe(switchMap(), tap()).

这篇关于如何在链接的 observables 之间传递结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆