ro

repository·main·Indexed 20 days ago

https://github.com/samber/ro

A Go implementation of the ReactiveX specification for processing infinite data streams in event-driven and asynchronous applications. The library includes an Enterprise Edition with features such as a license management system, an OpenTelemetry plugin for tracing, metrics, and logging, and a Prometheus plugin for observability.

Tokens
340.2K
Snippets
1.1K
Records
1.3K
Agent score
72%

What's inside ro

  1. Use sort plugin operators in ro streams

    main
    The sort sub-package provides operators to order items within an Observable. These operators allow you to manipulate the order of items emitted downstream in your reactive pipelines using various strategies such as custom comparators, top-N selection, or stable sorting.
  2. Use HTTP Client plugin operators

    main
    The http/client sub-package provides operators for issuing HTTP requests within ro reactive streams. These operators allow you to perform GET, POST, and streaming requests, emitting the resulting responses, JSON data, or raw bytes as Observable values.
  3. Use FSNotify plugin operators

    main
    The fsnotify sub-package provides plugin operators for ro that allow you to detect filesystem changes. These operators use fsnotify under the hood to monitor directories or files and emit filesystem events (such as create, write, and remove) as Observable values within a reactive stream.
  4. What is a Subject and how does it work?

    main

    A Subject is a special type that acts as both an Observable and an Observer. It serves as a bridge or proxy that can multicast values to multiple observers, making it the primary tool for implementing 'hot' observables and event broadcasting patterns.

    Key characteristics:

    • Observable: It can be subscribed to like any other observable.
    • Observer: It can receive values via Next, Error, and Complete methods.
    • Multicaster: It broadcasts the same values to all its subscribers.
    • Hot by nature: Values are shared among all subscribers concurrently, unlike 'cold' observables where each subscription gets its own independent stream of data.
    type Subject[T any] interface {
        Observable[T]
        Observer[T]
    
        HasObserver() bool
        CountObservers() int
    }
  5. What is an Observer and how does it work?

    main

    An Observer is the consumer side of reactive programming in samber/ro. It is the destination for values emitted by an Observable.

    Key characteristics:

    • Consumer of values: Receives values emitted by Observables.
    • Notification handler: Processes Next, Error, and Complete notifications.
    • Stateful: Tracks whether it is active, completed, or errored.
    • Thread-safe: Multiple goroutines can safely call Observer methods.

    An Observer follows a specific lifecycle: it starts as Active, and once it receives either an Error or a Complete notification, it transitions to a terminal state where no further notifications are accepted.

  6. What is ro and the Reactive Programming paradigm?

    main

    ro is a reactive programming library for Go that implements Observable streams. It is inspired by ReactiveX patterns and is designed to handle event-driven logic by treating events as streams that can be observed, transformed, and composed.

    Key capabilities include:

    • Handling asynchronous events naturally.
    • Transforming and composing data streams declaratively.
    • Managing backpressure and resource usage.
    • Building responsive and resilient applications.

    It is conceptually similar to samber/lo, but focused on events rather than collections.

  7. What is an Observable in ro?

    main

    An Observable is the core abstraction in ro representing a push-based stream of values over time. It acts as both a data producer and a factory for streams.

    Key Characteristics:

    • Producer of values: Emits zero or more values.
    • Stream factory: Each subscription triggers a new, independent execution (for cold observables).
    • Lazy: Values are only produced when a subscription occurs.
    • Push-based: The producer pushes values to observers rather than the consumer pulling them.

    Notification Types:

    1. Next: Emits a value from the sequence.
    2. Error: Emits an error and terminates the stream.
    3. Complete: Signals successful completion and terminates the stream.

    Note: Once an Error or Complete notification is emitted, no further values will be produced.

  8. What is a Subscription and how to use it

    main

    A Subscription manages the execution of an Observable. It acts as a resource manager for cleanup, a cancellation token to stop operations, and a lifecycle controller. It is thread-safe and can be used across multiple goroutines.

    To prevent resource leaks, always capture the Subscription returned by .Subscribe() and call .Unsubscribe() when the operation is no longer needed.

    // Create an Observable
    observable := ro.Interval(1 * time.Second)
    
    // Subscribe and get the subscription
    subscription := observable.Subscribe(ro.OnNext(func(tick int64) {
        fmt.Println("Tick:", tick)
    }))
    
    // Cancel the subscription to prevent leaks
    subscription.Unsubscribe()
  9. Handle error propagation in merged Observables

    main

    When using MergeWith, an error emitted by any of the participating Observables will cause the entire merged stream to fail and terminate. This is useful for ensuring that a failure in any part of a combined stream is treated as a failure of the whole.

    To handle this, ensure your Subscribe call includes an error handler.

    obs := ro.Pipe[int, int](
        ro.Just(1, 2, 3),
        ro.MergeWith(
            ro.Pipe[int, int](
                ro.Just(4, 5, 6),
                ro.MapErr(func(i int) (int, error) {
                    if i == 5 {
                        return 0, fmt.Errorf("error on 5")
                    }
                    return i, nil
                }),
            ),
        ),
    )
    
    sub := obs.Subscribe(ro.NewObserver(
        func(value int) {
            fmt.Printf("Next: %d\n", value)
        },
        func(err error) {
            fmt.Printf("Error: %v\n", err)
        },
        func() {
            fmt.Println("Completed")
        },
    ))