【问题标题】:How to merge "static" observable with "dynamic" in rxjs 6?如何在 rxjs 6 中将“静态”可观察与“动态”合并?
【发布时间】:2019-02-15 16:53:49
【问题描述】:

我正在编写 chrome 扩展程序,并决定将所有内容包装到流中,主要用于培训并查看 rxjs。

所以我遇到了这样一个问题。我有两个流,一个是静态 (不知道如何正确命名),一个是动态

静态流是仅在您订阅它时才发出值的流(getPath$),它从 chrome 存储中返回值。 动态(message$)流是监听事件(消息)的流。

所以我的目标是以某种方式合并这两个流,并且每次当我收到来自 message$ 的消息时,都从 getPath$ 获取值并进行一些计算基于两个值。

type Handler = (
    message: any, 
    sender: any, 
    sendResponse: (response: any) => void
) => void; 

type Values  = string | string[];

const getValues = (values: Values) => (cb) => chrome.storage.local.get(values, cb) 
const getPathBinded = bindCallback(getValues('path')) 
const getPath$ = getPathBinded() 

const message$ = fromEventPattern( 
   (handler: Handler) => chrome.runtime.onMessage.addListener(handler), 
   (handler: Handler) => chrome.runtime.onMessage.removeListener(handler), 
   (message, sender, sendResponse) => ({ message, sender, sendResponse }) 
).pipe( 
    map(
       ({ message }) => message
    ) 
) 

我发现了如何以这种方式做到这一点:

message$.pipe( 
    switchMap( 
        _ => getPath$, 
        (oV, iV) => {
            //doing some calculation
        } 
    ) 
 )

或者

combineLatest(
  message$,
  getPath$,
).pipe(
    map((a, b) => {
       //doing some calculation
    })
)

似乎它正在工作,但感觉就像我做错了。有什么建议或如何遵循最佳方法吗?另外,请纠正我的定义。

UPD 这里是完整的code: https://gist.github.com/ 以及最新版本的工作

const merged$ = message$.pipe(
   switchMap(sendedPath => combineLatest(
        path$,
        unit$
    ).pipe(
        map(([path, unit]) => [sendedPath, path, unit]))
    )
)

【问题讨论】:

  • 好吧,你的方法是对还是错取决于你想做什么......
  • 是否应该在每条新消息中重新计算chrome.storage.local.get(values, cb),或者对所有消息都使用第一个?
  • @OlesSavluk 它应该每次都重新计算新消息

标签: typescript google-chrome-extension rxjs rxjs6


【解决方案1】:

如果你的目标是

合并这两个流,每次我收到来自的消息时 message$ 从 getPath$ 获取值并根据两个值进行一些计算

看起来switchMap 方法是正确的。 switchMap 在这种情况下意味着:每当我收到来自 message$ 的通知时,我都会切换到另一个 Observable,在这种情况下,通过调用 getPath$ 返回一个。

如果您想对这两个值都做某事,那么您可能无法忽略message$ 通知的值,这是您的代码当前所做的,您必须将map 传递到结果中getPath$ 以便您可以创建一个包含 2 个 Observables 通知的 2 个值的对象,并将该对象传递给以下计算。代码应该是这样的

message$.pipe( 
    switchMap( 
        message => getPath$.pipe(map(path => ({message, path}))), 
        map(({message, path}) => {
            //doing some calculation
        })
    ) 
 )

【讨论】:

  • 是的,我需要对这两个值做一些事情。据我了解,switchMap 正在从一个可观察对象切换到另一个对象。这不正是我想要的。我希望它们同时返回一些值,并基于这些值做一些工作。
  • “同时返回一些值”是什么意思?我知道message$ 在发生某些事情时会发出,并且在它发出的任何时候您都想调用getPath$ 并使用这两个值做一些事情。这就是我建议的代码应该做的。顺便说一句,我更正了一个类型。我忘了在switchMap 之后添加map 运算符。
  • thnx 的答案似乎是我的方式。正如我从文档中了解到的那样,您可以将第二个参数作为第二个参数传递给 switchMap resultSelector,它可以访问内部和外部的值,所以第二个参数 map() 是多余的或者我错了?
【解决方案2】:

你所说的动态/静态类似于Hot and Cold Observables,但不完全相同。

您使用switchMap 的第一种方法基本上没问题,只是您应该每次都重新计算:

const combined$ = messages$.pipe(
  mergeMap(() => getPathBinded(), (message, path) => { ... })
)

我也把switchMap改成了mergeMap,否则你可能会“丢失”一些消息,见RxJS: Avoiding switchMap-Related Bugs


你也可以稍微简化你的代码:

const getStorage = bindCallback(chrome.storage.local.get); 
// use it as: const path$ = getStorage("path")

const message$ = fromEvent(chrome.runtime.onMessage);    
// should be enough, since you are only using default methods

【讨论】:

  • 我不确定如何正确命名此动态/静态、被动/主动、热/冷。我会试着解释我的意思。动态的或主动的——它是一种独立工作的可观察的。例如,如果您不取消订阅,则将永远收听事件。被动或静态——它仅在您需要时返回值(请求它)。我完全确信这个定义是错误的,但我不知道如何正确解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-18
  • 1970-01-01
  • 1970-01-01
  • 2018-02-04
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
相关资源
最近更新 更多