spotify / mobius

A functional reactive framework for managing state evolution and side-effects.
https://spotify.github.io/mobius/
Apache License 2.0
1.23k stars 97 forks source link

Add ExecutionPolicy to SubtypeEffectHandler #176

Closed juanmrivero closed 9 months ago

juanmrivero commented 9 months ago

This PR introduces the parameter executionPolicy: ExecutionPolicy too all builder functions in CoroutinesSubtypeEffectHandlerBuilder.

ExecutionPolicy gives users simple policies to control concurrency while processing effects for each effect type in the builder. The current implemented policies are:

Here is an example of usage:

    Mobius.loop(
            Update(...),
            MobiusCoroutines.subtypeEffectHandler<Effect, Event>()
              .addAction<Effect.Log>(executionPolicy = RunConcurrently) { ... }
              .addConsumer<Effect.UpdateSearchQuery>(executionPolicy = CancelPrevious){ effect -> ... }
              .addFunction<Effect.AddToBag>(executionPolicy = RunSequentially) { effect -> event }
             .build()
    )

By default all functions use RunSequentially. This was selected to avoid unintended cancellations or concurrent problems while processing an effect.

To support this feature the signature of the method addEffectHandler(kClass: KClass<out F>, function: suspend (F) -> Flow<E>) was changed to addEffectHandler(kClass: KClass<out F>, effectHandler: EffectHandler<F, E>). This is non backwards compatible. I'm assuming this is ok, since the method addFlowProducer, or addFlow (introduced in this PR) are analogous and easier to use.

Additionally the function addEffectHandler(kClass: KClass<out F>, effectHandler: EffectHandler<F, E>) allows the user to implement its own concurrency policy by handling the effect & event channels directly in case it needs more fine grained control.


This PR also introduces an utility function MobiusCoroutines.effectHandler( coroutineContext: CoroutineContext, onEffect: suspend (effect: F, eventConsumer: Consumer<E>) -> Unit) which creates a simple effect handler that will run all effects concurrently.

Here is an example of usage:

      Mobius.loop(
            Update(...),
            MobiusCoroutines.effectHandler<Effect, Event>(){ effect, eventConsumer ->
                when(effect) {
                    is Effect.Log -> { ... }
                    is Effect.AddToBag -> {
                        ...
                        eventConsumer.accept(Event.AddToBagSuccess)
                    }
                }
            }
    )
juanmrivero commented 9 months ago

@pettermahlen I updated the version to 2.0.0 and updated the baselineVersion in binary_compatibility.gradle to that version.

About if japicmp is enabled for mobius-coroutines, I understand by this line that it is, since the module applies the java-library plugin