it-roy-ru.com

Почему andThen of Future не цепляет результат?

Значение andThen, которое я узнал из этого ответ , является составителем функций.

Скажи это

f andThen g andThen h

будет равно

h(g(f(x)))

Это означает, что h function будет получать входные данные от g(f(x))

Но для andThen в Future, все закрытия следующего andThen всегда получают результат из исходного Future.

Future{
    1
}.andThen{ case Success(x) =>
    println(x) // print 1
    Thread.sleep(2000)
    x * 2
}.andThen{ case Success(x) =>
    println(x) // print 1
    Thread.sleep(2000)
    x * 2
}

сравнить с

val func: Function1[Int, Int] = { x: Int =>
  x
}.andThen { y =>
  println(y) // print 1
  y * 2
}.andThen { z =>
  println(z) // print 2
  z * 2
}
func(1)

В чем причина того, что Future :: andThen (s) получает все тот же результат из исходного Future вместо цепочки Future? Я заметил, что эти цепочки и затем будут выполняться последовательно, поэтому причина может быть не в параллельной цели.

17
Chen OT

scala.concurrent.Future разработан как компромисс двух асинхронных подходов:

  1. Объектно-ориентированный наблюдатель который позволяет связывать асинхронные обработчики
  2. Функциональный монада который предлагает богатые функциональные возможности композиции.

Чтение Future.andThen 's документов :

Применяет побочную функцию к результату этого будущего и возвращает новое будущее с результатом этого будущего.

Так что andThen, скорее всего, из вселенной OOP. Чтобы получить аналогичный результат, похожий на Function1.andThen, вы можете использовать map метод:

Future(1).map {_ * 2}.map {_ * 2}

andThen отличается от onComplete одной маленькой вещью: результирующее Future of andThen по-прежнему возвращает тот же результат, но будет ждать, пока предоставленный наблюдатель вернет или выбросит что-то. Вот почему там написано в документах:

Этот метод позволяет обеспечить выполнение обратных вызовов в указанный заказ.

Также обратите внимание на третью строку из документов:

Обратите внимание, что если один из обратных вызовов andThen выдает исключение, это исключение не распространяется на последующие и затем обратные вызовы . Вместо этого последующим обратным вызовам andThen присваивается исходное значение этого будущего.

Таким образом, он «полностью ничего не делает с новым результатом Future». Не мог даже испортить это с его собственным исключением. Это andThen и onComplete просто последовательная и параллельная привязка наблюдателей.

21
Odomontois

Позвольте мне подвести итог этой хорошей дискуссии.

Скажем, у нас есть tf: Future[T] =... и две функции, f: T => U и g: U => V

Мы можем сделать vf: Future[V] = tf map f map g, так же, как vf: Future[V] = tf map (f andThen g)

В другом случае использования, имея fp: PartialFunction[T, U] и gp: PartialFunction[U, V], Мы можем запустить tf1: Future[T] = tf andThen fp andThen gp - эти частичные функции будут вызываться для значения, которое производит tf, без внешнего эффекта - только побочные эффекты случаются. Эта последовательность ожидает fp перед вызовом gp.

Еще одна будущая операция, onComplete, работает следующим образом: имея f: Try[T] => U, вызов tf onComplete f будет вызывать f, даже если будущее закончится с ошибкой; результат tf onComplete f имеет тип Unit.

Кроме того, если ваша функция f создает Future, вам нужно будет использовать flatMap.

1
Vlad Patryshev