All Downloads are FREE. Search and download functionalities are using the official Maven repository.

monix.execution.FutureUtils.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014-2019 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.execution

import java.util.concurrent.TimeoutException
import monix.execution.schedulers.TrampolineExecutionContext.immediate
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.util.{Success, Try}

/** Utilities for Scala's standard `concurrent.Future`. */
object FutureUtils extends internal.FutureUtilsForPlatform {
  /** Utility that returns a new Future that either completes with
    * the original Future's result or with a TimeoutException in case
    * the maximum wait time was exceeded.
    *
    * @param atMost specifies the maximum wait time until the future is
    *               terminated with a TimeoutException
    * @param s is the Scheduler, needed for completing our internal promise
    * @return a new future that will either complete with the result of our
    *         source or fail in case the timeout is reached.
    */
  def timeout[A](source: Future[A], atMost: FiniteDuration)(implicit s: Scheduler): Future[A] = {
    val err = new TimeoutException
    val promise = Promise[A]()
    val task = s.scheduleOnce(atMost.length, atMost.unit, new Runnable { def run() = promise.tryFailure(err) })

    source.onComplete { r =>
      // canceling task to prevent waisted CPU resources and memory leaks
      // if the task has been executed already, this has no effect
      task.cancel()
      promise.tryComplete(r)
    }

    promise.future
  }

  /** Utility that returns a new Future that either completes with
    * the original Future's result or after the timeout specified by
    * `atMost` it tries to complete with the given `fallback`.
    * Whatever `Future` finishes first after the timeout, will win.
    *
    * @param atMost specifies the maximum wait time until the future is
    *               terminated with a TimeoutException
    * @param fallback the fallback future that gets triggered after timeout
    * @param s is the Scheduler, needed for completing our internal promise
    * @return a new future that will either complete with the result of our
    *         source or with the fallback in case the timeout is reached
    */
  def timeoutTo[A](source: Future[A], atMost: FiniteDuration, fallback: => Future[A])(
    implicit s: Scheduler): Future[A] = {

    val promise = Promise[Option[Try[A]]]()
    val task = s.scheduleOnce(atMost.length, atMost.unit, new Runnable { def run() = promise.trySuccess(None) })

    source.onComplete { r =>
      // canceling task to prevent waisted CPU resources and memory leaks
      // if the task has been executed already, this has no effect
      task.cancel()
      promise.trySuccess(Some(r))
    }

    promise.future.flatMap {
      case Some(res) => Future.fromTry(res)
      case None =>
        // evaluate fallback only here to exclude possibility of race condition
        // between source and fallback when they are finishing at the same time
        fallback
    }
  }

  /** Utility that lifts a `Future[A]` into a `Future[Try[A]]`, exposing
    * error explicitly.
    */
  def materialize[A](source: Future[A])(implicit ec: ExecutionContext): Future[Try[A]] = {
    source.transform(t => Success(t))(immediate)
  }

  /** Given a mapping functions that operates on successful results as well as
    * errors, transforms the source by applying it.
    *
    * Similar to `Future.transform` from Scala 2.12.
    */
  def transform[A, B](source: Future[A], f: Try[A] => Try[B])(implicit ec: ExecutionContext): Future[B] =
    source.transform(f)

  /** Given a mapping functions that operates on successful results
    * as well as errors, transforms the source by applying it.
    *
    * Similar to `Future.transformWith` from Scala 2.12.
    */
  def transformWith[A, B](source: Future[A], f: Try[A] => Future[B])(implicit ec: ExecutionContext): Future[B] =
    source.transformWith(f)

  /** Utility that transforms a `Future[Try[A]]` into a `Future[A]`,
    * hiding errors, being the opposite of [[materialize]].
    */
  def dematerialize[A](source: Future[Try[A]])(implicit ec: ExecutionContext): Future[A] = {
    source.map(_.get)(immediate)
  }

  /** Creates a future that completes with the specified `result`, but only
    * after the specified `delay`.
    */
  def delayedResult[A](delay: FiniteDuration)(result: => A)(implicit s: Scheduler): Future[A] = {
    val p = Promise[A]()
    s.scheduleOnce(delay.length, delay.unit, new Runnable { def run() = p.complete(Try(result)) })
    p.future
  }

  /** Provides extension methods for `Future`. */
  object extensions {
    /** Provides utility methods added on Scala's `concurrent.Future` */
    implicit class FutureExtensions[A](val source: Future[A]) extends AnyVal {
      /** [[FutureUtils.timeout]] exposed as an extension method. */
      def timeout(atMost: FiniteDuration)(implicit s: Scheduler): Future[A] =
        FutureUtils.timeout(source, atMost)

      /** [[FutureUtils.timeoutTo]] exposed as an extension method. */
      def timeoutTo[U >: A](atMost: FiniteDuration, fallback: => Future[U])(implicit s: Scheduler): Future[U] =
        FutureUtils.timeoutTo(source, atMost, fallback)

      /** [[FutureUtils.materialize]] exposed as an extension method. */
      def materialize(implicit ec: ExecutionContext): Future[Try[A]] =
        FutureUtils.materialize(source)

      /** [[FutureUtils.dematerialize]] exposed as an extension method. */
      def dematerialize[U](implicit ev: A <:< Try[U], ec: ExecutionContext): Future[U] =
        FutureUtils.dematerialize(source.asInstanceOf[Future[Try[U]]])
    }

    /** Provides utility methods for Scala's `concurrent.Future` companion object. */
    implicit class FutureCompanionExtensions(val f: Future.type) extends AnyVal {
      /** [[FutureUtils.delayedResult]] exposed as an extension method. */
      def delayedResult[A](delay: FiniteDuration)(result: => A)(implicit s: Scheduler): Future[A] =
        FutureUtils.delayedResult(delay)(result)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy