如何在链式可观察物之间传递结果 [英] How to pass results between chained observables

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

问题描述

抽象问题:每次源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))需要接收所有先前的值,以便即使方法本身不需要它也可以将它们传递给下一个管道
  • 需要内部可观测对象(在方法内)以映射值,以便其他管道阶段可以破坏它们:
  • 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
  }

如何在链接的可观察对象之间传递结果而没有出现我提到的问题?我错过了一个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!

您的主要问题:

如何在链接的可观察对象之间传递结果而又没有我提到的问题?

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())

Notice the signature of the first is userId$.pipe(switchMap(), map(), map(), tap())

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

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

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