Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to avoid calling map function multiple times? #31

Closed
zsoerenm opened this issue Aug 24, 2023 · 2 comments
Closed

How to avoid calling map function multiple times? #31

zsoerenm opened this issue Aug 24, 2023 · 2 comments

Comments

@zsoerenm
Copy link
Contributor

Here is a MWE:

fooS = RecentSubject(String)
mappedFooS = fooS |> map(String, x -> begin println("Called"); x end)
subscribe!(mappedFooS, logger())
subscribe!(mappedFooS, logger())
next!(fooS, "Test")

Output

Called
[LogActor] Data: Test
Called
[LogActor] Data: Test

I intentionally subscribed to mappedFooS twice. Is doesn't need to be a subscription, it could also be any other map function with a subscription. The problem is that the map function inside mappedFooS get called twice even though it only needs to be called once in theory. If the map function does some heavy calculation things can become quite slow.
Is there a solution to this?

@bvdmitri
Copy link
Member

@zsoerenm What you are looking for is the share() operator.

fooS = RecentSubject(String)
# Added the `|> share()` operator
mappedAndSharedFooS = fooS |> map(String, x -> begin println("Called"); x end) |> share()
subscribe!(mappedAndSharedFooS, logger())
subscribe!(mappedAndSharedFooS, logger())
next!(fooS, "Test")

Output

Called # appears only once
[LogActor] Data: Test
[LogActor] Data: Test

The map behaviour is by design and it always creates a new instance of the observable which processes data independently. The share() operator changes the behaviour and "shares" the observable between different subscribers.

Under the hood, the share() operator maintains a single copy of the mapped observable and simply resends the computed value to all the subscribers. This, however, might be confusing if the observable completes before all subscribes have subscribed. For example:

Without share() we get two independent copies and get independent completion events for both subscribers.

# I use the `from` instead of a `Subject`, which sends all data at once and completes immediately 
fooS = from([ "Hello", "world!" ]) 
mappedFooS = fooS |> map(String, x -> begin println("Called"); x end)
subscribe!(mappedFooS, logger())
subscribe!(mappedFooS, logger())

# Called
# [LogActor] Data: Hello
# Called
# [LogActor] Data: world!
# [LogActor] Completed
# Called
# [LogActor] Data: Hello
# Called
# [LogActor] Data: world!
# [LogActor] Completed

With share() the first subscribers completes the underlying shared observable and the second subscriber receives nothing.

fooS = from([ "Hello", "world!" ])
mappedAndSharedFooS = fooS |> map(String, x -> begin println("Called"); x end) |> share()
subscribe!(mappedAndSharedFooS, logger())
subscribe!(mappedAndSharedFooS, logger())

# Called
# [LogActor] Data: Hello
# Called
# [LogActor] Data: world!
# [LogActor] Completed
# [LogActor] Completed

The second actor will not receive any shared values, because the first actor exhausted the observable till its completion stage.

@zsoerenm zsoerenm changed the title How to avoid calling mapped function multiple times? How to avoid calling map function multiple times? Aug 24, 2023
@zsoerenm
Copy link
Contributor Author

Thank you for clarifying!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants