typescript - چگونه یک observable «ایستا» را با «پویا» در rxjs 6 ترکیب کنیم؟


من در حال نوشتن افزونه کروم هستم و تصمیم گرفتم همه چیز را در قالب استریمها قرار دهم، عمدتاً برای آموزش و نگاهی به rxjs.

پس من با چنین مشکلی مواجه هستم. من دو جریان دارم، یک جریان ثابت (نمیدانم چگونه آن را درست نامگذاری کنم) و یک جریان پویا.

یک جریان ایستا جریانی است که تنها زمانی مقدار تولید میکند که شما آن را مشترک شوید (getPath$)، و مقدار را از حافظه کروم بازمیگرداند. یک جریان پویا (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
    })
)

به نظر میرسد که کار میکند، اما احساس میکنم دارم اشتباه انجام میدهم. پیشنهادی دارید یا چطور میتوانم آن را طبق بهترین روشها انجام دهم؟ همچنین لطفاً تعاریف من را اصلاح کنید.

بهروزرسانی اینجا کد کامل است: https://gist.github.com/ و جدیدترین نسخه که کار میکند

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

پاسخ شماره ۱

چیزی که شما دینامیک/استاتیک مینامید شبیه به قابل مشاهدههای گرم و سرد است، اما دقیقا همان نیست.

رویکرد اول شما با switchMap عمدتاً درست است، به جز اینکه باید هر بار آن را دوباره محاسبه کنید:

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

من همچنین switchMap را به mergeMap تغییر دادهام، در غیر این صورت، ممکن است برخی از پیامها را «از دست بدهید»، ببینید RxJS: جلوگیری از باگهای مرتبط با switchMap

و همچنین ممکن است کد خود را کمی سادهتر کنید:

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

پاسخ شماره ۲

اگر هدف شما این است

برای ادغام این دو جریان و هر بار که پیامی از message$ دریافت میکنم، مقدار را از getPath$ بگیر و محاسبات را بر اساس این دو مقدار انجام بده

به نظر میرسد که رویکرد switchMap درست است. switchMap در این مورد به این معناست: هر بار که یک اعلان از message$ دریافت میکنم، به Observable دیگر سوئیچ میکنم، که در این مورد همان چیزی است که با فراخوانی getPath$ برگردانده میشود.

اگر سپس بخواهید با هر دو مقدار کاری انجام دهید، احتمالاً نمیتوانید مقدار اطلاعرسانیشده توسط message$ را نادیده بگیرید، که کاری است که کد شما در حال حاضر انجام میدهد، و باید یک map را در نتیجه getPath$ اعمال کنید تا بتوانید شیئی ایجاد کنید که شامل ۲ مقدار اطلاعرسانیشده توسط ۲ Observable باشد و این شیء را به محاسبه بعدی منتقل کنید. کد باید شبیه به

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