Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2014-2020 by The Monix Project Developers.
* See the project homepage at: https://monix.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package monix.reactive
import cats.Contravariant
import cats.arrow.Profunctor
import monix.eval.{Task, TaskLike}
import monix.execution.cancelables.AssignableCancelable
import monix.execution.{Callback, Cancelable, Scheduler}
import monix.reactive.internal.consumers._
import monix.reactive.observers.Subscriber
import scala.collection.mutable
/** The `Consumer` is a specification of how to consume an observable.
*
* It is a factory of subscribers with a completion callback attached,
* being effectively a way to transform observables into
* [[monix.eval.Task tasks]] for less error prone consuming of streams.
*/
abstract class Consumer[-In, +R] extends (Observable[In] => Task[R]) with Serializable { self =>
/** Builds a new [[monix.reactive.observers.Subscriber Subscriber]]
* that can be subscribed to an [[Observable]] for consuming a stream,
* with a callback that should eventually get called with a materialized
* result.
*
* Notes:
*
* - calling the callback must obey the contract for the
* [[monix.execution.Callback Callback]] type
* - the given callback should always get called, unless the
* upstream gets canceled
* - the given callback can be called when the subscriber is
* finished processing, but not necessarily
* - if the given callback isn't called after the subscriber is
* done processing, then the `Task` returned by [[apply]]
* loses the ability to cancel the stream, as that `Task` will
* complete before the stream is finished
*
* @param cb is the [[monix.execution.Callback Callback]] that will get
* called once the created subscriber is finished.
* @param s is the [[monix.execution.Scheduler Scheduler]] that will
* get used for subscribing to the source observable and to
* process the events.
*
* @return a new subscriber that can be used to consume observables.
*/
def createSubscriber(cb: Callback[Throwable, R], s: Scheduler): (Subscriber[In], AssignableCancelable)
/** Given a source [[Observable]], convert it into a [[monix.eval.Task Task]]
* by piggybacking on [[createSubscriber]].
*/
final def apply(source: Observable[In]): Task[R] =
Task.create { (scheduler, cb) =>
val (out, consumerSubscription) = createSubscriber(cb, scheduler)
// Start consuming the stream
val sourceSubscription = source.subscribe(out)
// Assign the observable subscription to our assignable,
// thus the subscriber can cancel its subscription
consumerSubscription := sourceSubscription
// We might not return the assignable returned by `createSubscriber`
// because it might be a dummy
if (consumerSubscription.isInstanceOf[Cancelable.IsDummy])
sourceSubscription
else
consumerSubscription
}
/** Given a contravariant mapping function, transform
* the source consumer by transforming the input.
*/
final def contramap[In2](f: In2 => In): Consumer[In2, R] =
new ContraMapConsumer[In2, In, R](self, f)
/** Given a function that transforms the input stream, uses it
* to transform the source consumer into one that accepts events
* of the type specified by the transformation function.
*/
final def transformInput[In2](f: Observable[In2] => Observable[In]): Consumer[In2, R] =
new TransformInputConsumer[In2, In, R](self, f)
/** Given a mapping function, when consuming a stream,
* applies the mapping function to the final result,
* thus modifying the output of the source consumer.
*
* Note that for applying the mapping function an
* asynchronous boundary is forced, otherwise it could
* trigger a stack overflow exception. For more efficient
* mapping of the result, it's probably better to `map`
* the resulting `Task` on [[Observable.consumeWith]].
*
* @see [[mapTask]] for a variant that can map the output
* to a `Task` that can be processed asynchronously.
*/
final def map[R2](f: R => R2): Consumer[In, R2] =
new MapConsumer[In, R, R2](self, f)
/** Given a mapping function, when consuming a stream,
* applies the mapping function to the final result,
* thus modifying the output of the source consumer.
*
* The mapping function returns results using a generic `F[_]`
* data type that must implement the `cats.effect.Effect` type
* class. Examples of such classes are `cats.effect.IO` and
* [[monix.eval.Task]], thus being able to do asynchronous
* processing.
*
* See [[mapTask]] for the version that's specialized on `Task`.
*/
final def mapEval[F[_], R2](f: R => F[R2])(implicit F: TaskLike[F]): Consumer[In, R2] =
new MapTaskConsumer[In, R, R2](self, r => F(f(r)))
/** Given a mapping function, when consuming a stream,
* applies the mapping function to the final result,
* thus modifying the output of the source consumer.
*
* The mapping function returns a [[monix.eval.Task Task]]
* that can be used to process results asynchronously.
*
* Note that for applying the mapping function an
* asynchronous boundary is forced, otherwise it could
* trigger a stack overflow exception. For more efficient
* mapping of the result, it's probably better to `map`
* the resulting `Task` on [[Observable.consumeWith]].
*
* See [[mapEval]] for the version that can work with any
* data type that implements `cats.effect.Effect`.
*/
final def mapTask[R2](f: R => Task[R2]): Consumer[In, R2] =
new MapTaskConsumer[In, R, R2](self, f)
}
/** The companion object of [[Consumer]], defines consumer builders.
*
* @define loadBalanceDesc Creates a consumer that, when consuming
* the stream, will start multiple subscribers corresponding
* and distribute the load between them.
*
* Once each subscriber emits a final result, this consumer will
* return a list of aggregated results.
*
* Has the following rules:
*
* - items are pushed on free subscribers, respecting their
* contract, each item being pushed to the first available
* subscriber in the queue
* - in case no free subscribers are available, then the
* source gets back-pressured until free subscribers are
* available
* - in case of `onComplete` or `onError`, all subscribers
* that are still active will receive the event
* - the `onSuccess` callback of individual subscribers is
* aggregated in a list buffer and once the aggregate contains
* results from all subscribers, the load-balancing consumer
* will emit the aggregate
* - the `onError` callback triggered by individual subscribers will
* signal that error upstream and cancel the streaming for
* every other subscriber
* - in case any of the subscribers cancels its subscription
* (either returning `Stop` in `onNext` or canceling its assigned
* cancelable), it gets excluded from the pool of active
* subscribers, but the other active subscribers will still
* receive notifications
* - if all subscribers canceled (either by returning `Stop`
* or by canceling their assignable cancelable reference),
* then streaming stops as well
*
* In other words the `Task`, created by applying this consumer to
* an observable, will complete once all the subscribers emit a result
* or as soon as an error happens.
*
* @define loadBalanceReturn a list of aggregated results that
* were computed by all of the subscribers as their result
*/
object Consumer {
/** Creates a [[Consumer]] out of the given function.
*
* The function returns an [[Observer]] and takes as input:
*
* - a [[monix.execution.Scheduler Scheduler]] for any asynchronous
* execution needs the returned observer might have
* - a [[monix.execution.Cancelable Cancelable]] that can be used for
* concurrently canceling the stream (in addition to being able to
* return `Stop` from `onNext`)
* - a [[monix.execution.Callback Callback]] that must be called
* to signal the final result, after the observer finished
* processing the stream, or an error if the processing finished
* in error
*
* @param f is the input function with an injected `Scheduler`,
* `Cancelable`, `Callback` and that returns an `Observer`
*/
def create[In, Out](f: (Scheduler, Cancelable, Callback[Throwable, Out]) => Observer[In]): Consumer[In, Out] =
new CreateConsumer[In, Out](f)
/** Given a function taking a `Scheduler` and returning an [[Observer]],
* builds a consumer from it.
*
* You can use the `Scheduler` as the execution context, for working
* with `Future`, for forcing asynchronous boundaries or for executing
* tasks with a delay.
*/
def fromObserver[In](f: Scheduler => Observer[In]): Consumer[In, Unit] =
new FromObserverConsumer[In](f)
/** A consumer that immediately cancels its upstream after subscription. */
def cancel[A]: Consumer.Sync[A, Unit] =
CancelledConsumer
/** A consumer that triggers an error and immediately cancels its
* upstream after subscription.
*/
def raiseError[In, R](ex: Throwable): Consumer.Sync[In, R] =
new RaiseErrorConsumer(ex)
/** A consumer that will return a collection of all the streamed
* values as a Scala `List`.
*
* Note that this method is functionally the same as the [[Observable.toListL]]
*
* WARNING: For infinite streams the process will eventually
* blow up with an out of memory error.
*/
def toList[A]: Consumer[A, List[A]] =
new FoldLeftConsumer[A, mutable.ListBuffer[A]](() => mutable.ListBuffer.empty[A], _ += _).map(_.toList)
/** Given a fold function and an initial state value, applies the
* fold function to every element of the stream and finally signaling
* the accumulated value.
*
* @param initial is a lazy value that will be fed at first
* in the fold function as the initial state.
* @param f is the function that calculates a new state on each
* emitted value by the stream, for accumulating state
*/
def foldLeft[S, A](initial: => S)(f: (S, A) => S): Consumer.Sync[A, S] =
new FoldLeftConsumer[A, S](initial _, f)
/** Given a fold function and an initial state value, applies the
* fold function to every element of the stream and finally signaling
* the accumulated value.
*
* The given fold function returns an `F[A]` value, where `F` is
* any data type that implements `cats.effect.Effect` (e.g. `Task`,
* `Coeval`), thus able to do asynchronous processing, with
* ordering of calls being guaranteed.
*
* @param initial is a lazy value that will be fed at first
* in the fold function as the initial state.
*
* @param f is the function that calculates a new state on each
* emitted value by the stream, for accumulating state,
* returning a `F[A]` capable of lazy or asynchronous
* execution.
*/
def foldLeftEval[F[_], S, A](initial: => S)(f: (S, A) => F[S])(implicit F: TaskLike[F]): Consumer[A, S] =
new FoldLeftTaskConsumer[A, S](initial _, (s, a) => F(f(s, a)))
/** Given a fold function and an initial state value, applies the
* fold function to every element of the stream and finally signaling
* the accumulated value.
*
* The given fold function returns a `Task` that can execute an
* asynchronous operation, with ordering of calls being guaranteed.
*
* @param initial is a lazy value that will be fed at first
* in the fold function as the initial state.
* @param f is the function that calculates a new state on each
* emitted value by the stream, for accumulating state,
* returning a `Task` capable of asynchronous execution.
*/
def foldLeftTask[S, A](initial: => S)(f: (S, A) => Task[S]): Consumer[A, S] =
new FoldLeftTaskConsumer[A, S](initial _, f)
/** A consumer that will produce the first streamed value on
* `onNext` after which the streaming gets cancelled.
*
* In case the stream is empty and so no `onNext` happen before
* `onComplete`, then the a `NoSuchElementException` will get
* triggered.
*/
def head[A]: Consumer.Sync[A, A] =
new HeadConsumer[A]
/** A consumer that will produce the first streamed value on
* `onNext` after which the streaming gets cancelled.
*
* In case the stream is empty and so no `onNext` happen before
* `onComplete`, then the a `NoSuchElementException` will get
* triggered.
*/
def headOption[A]: Consumer.Sync[A, Option[A]] =
new HeadOptionConsumer[A]
/** A consumer that will produce a [[Notification]] of the first value
* received (`onNext`, `onComplete` or `onError`), after which the
* streaming gets cancelled.
*
* - [[Notification.OnNext OnNext]] will be signaled on the first `onNext`
* event if it happens and the streaming will be stopped by `Stop`.
* - [[Notification.OnComplete OnComplete]] will be signaled if the stream
* was empty and thus completed without any `onNext`.
* - [[Notification.OnError OnError]] will be signaled if the stream
* was completed in error before the first `onNext` happened.
*/
def firstNotification[A]: Consumer.Sync[A, Notification[A]] =
new FirstNotificationConsumer[A]
/** A simple consumer that consumes all elements of the
* stream and then signals its completion.
*/
def complete[A]: Consumer.Sync[A, Unit] =
CompleteConsumer
/** Builds a consumer that will consume the stream, applying the given
* function to each element and then finally signaling its completion.
*
* @param cb is the function that will be called for each element
*/
def foreach[A](cb: A => Unit): Consumer.Sync[A, Unit] =
new ForeachConsumer[A](cb)
/** Builds a consumer that will consume the stream, applying the given
* function to each element and then finally signaling its completion.
*
* The given callback function returns a `F[A]` value that can
* execute an asynchronous operation, with ordering of calls being
* guaranteed, given that the `F[_]` data type is any type that
* implements `cats.effect.Effect` (e.g. `Task`, `IO`).
*
* @param cb is the function that will be called for each element
*/
def foreachEval[F[_], A](cb: A => F[Unit])(implicit F: TaskLike[F]): Consumer[A, Unit] =
foreachTask(a => F(cb(a)))
/** Builds a consumer that will consume the stream, applying the given
* function to each element and then finally signaling its completion.
*
* The given callback function returns a `Task` that can execute an
* asynchronous operation, with ordering of calls being guaranteed.
*
* @param cb is the function that will be called for each element
*/
def foreachTask[A](cb: A => Task[Unit]): Consumer[A, Unit] =
new ForeachAsyncConsumer[A](cb)
/** Builds a consumer that will consume the stream, applying the given
* function to each element, in parallel, then finally signaling its
* completion.
*
* @param parallelism is the maximum number of (logical) threads to use
* @param cb is the function that will be called for each element
*/
def foreachParallel[A](parallelism: Int)(cb: A => Unit): Consumer[A, Unit] =
loadBalance(parallelism, foreach(cb)).map(_ => ())
/** Builds a consumer that will consume the stream, applying the given
* function to each element, in parallel, then finally signaling its
* completion.
*
* The given callback function returns a `Task` that can execute an
* asynchronous operation, with ordering of calls being guaranteed
* per subscriber.
*
* @param parallelism is the maximum number of (logical) threads to use
* @param cb is the function that will be called for each element
*/
def foreachParallelTask[A](parallelism: Int)(cb: A => Task[Unit]): Consumer[A, Unit] =
loadBalance(parallelism, foreachTask(cb)).map(_ => ())
/** $loadBalanceDesc
*
* @param parallelism is the number of subscribers that will get
* initialized to process incoming events in parallel.
* @param consumer is the subscriber factory that will initialize
* all needed subscribers, in number equal to the specified
* parallelism and thus that will be fed in parallel
*
* @return $loadBalanceReturn
*/
def loadBalance[A, R](parallelism: Int, consumer: Consumer[A, R]): Consumer[A, List[R]] =
new LoadBalanceConsumer[A, R](parallelism, Array(consumer))
/** $loadBalanceDesc
*
* @param consumers is a list of consumers that will initialize
* the subscribers that will process events in parallel,
* with the parallelism factor being equal to the number
* of consumers specified in this list.
*
* @return $loadBalanceReturn
*/
def loadBalance[A, R](consumers: Consumer[A, R]*): Consumer[A, List[R]] =
new LoadBalanceConsumer[A, R](consumers.length, consumers.toArray)
/** Defines a synchronous [[Consumer]] that builds
* [[monix.reactive.observers.Subscriber.Sync synchronous subscribers]].
*/
trait Sync[-In, +R] extends Consumer[In, R] {
override def createSubscriber(cb: Callback[Throwable, R], s: Scheduler): (Subscriber.Sync[In], AssignableCancelable)
}
/** `cats.Contravariant` instance for [[Consumer]]. */
implicit def catsContravariant[C]: Contravariant[Consumer[?, C]] = new Contravariant[Consumer[?, C]] {
override def contramap[A, B](fa: Consumer[A, C])(f: B => A): Consumer[B, C] =
fa.contramap(f)
}
/** `cats.arrow.Profunctor` instance for [[Consumer]]. */
implicit val catsProfunctor: Profunctor[Consumer] = new Profunctor[Consumer] {
override def dimap[A, B, C, D](fab: Consumer[A, B])(f: C => A)(g: B => D): Consumer[C, D] =
fab.contramap(f).map(g)
}
}