স্কেল: ভবিষ্যতে [ভবিষ্যতে] তালিকা করুন [তালিকা] ব্যর্থ ফিউচার উপেক্ষা করে


116

আমি ফিউচারের একটি স্বেচ্ছাসেবী দৈর্ঘ্যের তালিকাকে ভবিষ্যতের তালিকায় রূপান্তর করার জন্য একটি উপায় খুঁজছি। আমি প্লেফ্রেমওয়ার্ক ব্যবহার করছি, সুতরাং শেষ অবধি, আমি যা চাই তা হ'ল একটি Future[Result], তবে বিষয়গুলিকে সহজ করে তুলতে, আসুন আমরা বলি এটি Future[List[Int]]করার সাধারণ উপায়টি ব্যবহার করা হবে Future.sequence(...)তবে একটি বাঁক আছে ... আমি যে তালিকাটি সাধারণত দেওয়া হয় তা হ'ল এতে প্রায় ১০-২০ ফিউচার থাকে এবং এই ফিউচারগুলির একটিরও ব্যর্থ হওয়া অস্বাভাবিক নয় (তারা বাহ্যিক ওয়েব পরিষেবাদির জন্য অনুরোধ করছে)। তাদের মধ্যে যে কোনও একটি ব্যর্থ হয় সেই ইভেন্টে তাদের সবার সাথে আবার চেষ্টা করার পরিবর্তে, আমি সফল হয়ে ওঠে এবং সেগুলি ফিরে পেতে সক্ষম হতে চাই।

উদাহরণস্বরূপ, নিম্নলিখিত কাজগুলি কাজ করে না

import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import scala.util.Failure

val listOfFutures = Future.successful(1) :: Future.failed(new Exception("Failure")) :: 
                    Future.successful(3) :: Nil

val futureOfList = Future.sequence(listOfFutures)

futureOfList onComplete {
  case Success(x) => println("Success!!! " + x)
  case Failure(ex) => println("Failed !!! " + ex)
}

scala> Failed !!! java.lang.Exception: Failure

একমাত্র ব্যতিক্রম না পেয়ে আমি সেখান থেকে 1 এবং 3 টানতে সক্ষম হতে চাই। আমি ব্যবহার করার চেষ্টা করেছি Future.fold, কিন্তু এটি স্পষ্টতই কল করেFuture.sequence পর্দার পিছনে করে।

সাহায্যের জন্য আগাম ধন্যবাদ!

উত্তর:


146

কৌতুকটি হ'ল প্রথমে নিশ্চিত করা যে ফিউচারগুলির কোনওটিই ব্যর্থ হয়েছে। .recoverএখানে আপনার বন্ধু, আপনি mapসমস্ত Future[T]ফলাফলকে রূপান্তর করতে এটির সাথে একত্রিত করতে পারেনFuture[Try[T]]] দৃষ্টান্তে , যার সবকটিই সফল ফিউচার হিসাবে নিশ্চিত।

দ্রষ্টব্য: আপনি এখানে Optionবা Eitherপাশাপাশি ব্যবহার করতে পারেন Tryতবে আপনি যদি ব্যতিক্রমগুলি ফাঁদে রাখতে চান তবে সবচেয়ে পরিষ্কার উপায়

def futureToFutureTry[T](f: Future[T]): Future[Try[T]] =
  f.map(Success(_)).recover { case x => Failure(x)}

val listOfFutures = ...
val listOfFutureTrys = listOfFutures.map(futureToFutureTry(_))

তারপরে Future.sequenceআগের মতো ব্যবহার করুন , আপনাকে এFuture[List[Try[T]]]

val futureListOfTrys = Future.sequence(listOfFutureTrys)

তারপরে ফিল্টার করুন:

val futureListOfSuccesses = futureListOfTrys.map(_.filter(_.isSuccess))

এমনকি আপনার যদি প্রয়োজন হয় তবে নির্দিষ্ট ব্যর্থতাগুলিও সরিয়ে ফেলতে পারেন:

val futureListOfFailures = futureListOfTrys.map(_.filter(_.isFailure))

ধন্যবাদ! .recoverসত্যিই আমার জন্য অনুপস্থিত টুকরা ছিল।
জো

20
প্রকার থেকে মুক্তি পাওয়ার _.collect{ case Success(x) => x}পরিবর্তে আপনি ব্যবহার করতে পারেন । _.filter(_.isSuccess)TryfutureListOfSuccesses
সেনিয়া

43
.recover(x => Failure(x)).recover({case e => Failure(e)})
স্কেলে

আমি মনে করি আপনি ভবিষ্যতের মোড়ক মিস করছেন: ডিফ ফিউচারটফিউশনঅফট্রি [এ] (চ: ভবিষ্যত [এ]): ভবিষ্যত [চেষ্টা করুন [এ]] = {ভাল পি = প্রতিশ্রুতি [চেষ্টা করুন [এ]] () f.map {a => পি.সুকসেস (স্কেলা.ইটিল.সুকসেস (ক)) re। পুনরুদ্ধার {কেস এক্স: থ্রোয়েবল => পি.সুচেস (ব্যর্থতা (এক্স))} পি.ফিউচার}
দারিও

তাই না আমি অন্য ভবিষ্যতের সাথে ভবিষ্যতের মানচিত্র করছি, একটি মধ্যবর্তী প্রতিশ্রুতির দরকার নেই এবং এটি অপচয় হবে
কেভিন রাইট

12

স্কেলা ২.১২ এর একটি উন্নতি হয়েছে Future.transformযা কম কোড সহ একটি আনসারে নিজেকে ধার দেয়।

val futures = Seq(Future{1},Future{throw new Exception})

// instead of `map` and `recover`, use `transform`
val seq = Future.sequence(futures.map(_.transform(Success(_)))) 

val successes = seq.map(_.collect{case Success(x)=>x})
successes
//res1: Future[Seq[Int]] = Future(Success(List(1)))

val failures = seq.map(_.collect{case Failure(x)=>x})
failures
//res2: Future[Seq[Throwable]] = Future(Success(List(java.lang.Exception)))

11

আমি কেভিনের উত্তরটি চেষ্টা করেছি, এবং আমি আমার স্কালার সংস্করণে (২.১.১.৫) এক গোলকের মধ্যে পড়ে গেলাম ... আমি এটি সংশোধন করেছি এবং কারও আগ্রহী হলে কয়েকটি অতিরিক্ত পরীক্ষা লিখেছি ... এখানে আমার সংস্করণ>

implicit class FutureCompanionOps(val f: Future.type) extends AnyVal {

    /** Given a list of futures `fs`, returns the future holding the list of Try's of the futures from `fs`.
      * The returned future is completed only once all of the futures in `fs` have been completed.
      */
    def allAsTrys[T](fItems: /* future items */ List[Future[T]]): Future[List[Try[T]]] = {
      val listOfFutureTrys: List[Future[Try[T]]] = fItems.map(futureToFutureTry)
      Future.sequence(listOfFutureTrys)
    }

    def futureToFutureTry[T](f: Future[T]): Future[Try[T]] = {
      f.map(Success(_)) .recover({case x => Failure(x)})
    }

    def allFailedAsTrys[T](fItems: /* future items */ List[Future[T]]): Future[List[Try[T]]] = {
      allAsTrys(fItems).map(_.filter(_.isFailure))
    }

    def allSucceededAsTrys[T](fItems: /* future items */ List[Future[T]]): Future[List[Try[T]]] = {
      allAsTrys(fItems).map(_.filter(_.isSuccess))
    }
}


// Tests... 



  // allAsTrys tests
  //
  test("futureToFutureTry returns Success if no exception") {
    val future =  Future.futureToFutureTry(Future{"mouse"})
    Thread.sleep(0, 100)
    val futureValue = future.value
    assert(futureValue == Some(Success(Success("mouse"))))
  }
  test("futureToFutureTry returns Failure if exception thrown") {
    val future =  Future.futureToFutureTry(Future{throw new IllegalStateException("bad news")})
    Thread.sleep(5)            // need to sleep a LOT longer to get Exception from failure case... interesting.....
    val futureValue = future.value

    assertResult(true) {
      futureValue match {
        case Some(Success(Failure(error: IllegalStateException)))  => true
      }
    }
  }
  test("Future.allAsTrys returns Nil given Nil list as input") {
    val future =  Future.allAsTrys(Nil)
    assert ( Await.result(future, 100 nanosecond).isEmpty )
  }
  test("Future.allAsTrys returns successful item even if preceded by failing item") {
    val future1 =  Future{throw new IllegalStateException("bad news")}
    var future2 = Future{"dog"}

    val futureListOfTrys =  Future.allAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys, 10 milli)
    System.out.println("successItem:" + listOfTrys);

    assert(listOfTrys(0).failed.get.getMessage.contains("bad news"))
    assert(listOfTrys(1) == Success("dog"))
  }
  test("Future.allAsTrys returns successful item even if followed by failing item") {
    var future1 = Future{"dog"}
    val future2 =  Future{throw new IllegalStateException("bad news")}

    val futureListOfTrys =  Future.allAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys,  10 milli)
    System.out.println("successItem:" + listOfTrys);

    assert(listOfTrys(1).failed.get.getMessage.contains("bad news"))
    assert(listOfTrys(0) == Success("dog"))
  }
  test("Future.allFailedAsTrys returns the failed item and only that item") {
    var future1 = Future{"dog"}
    val future2 =  Future{throw new IllegalStateException("bad news")}

    val futureListOfTrys =  Future.allFailedAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys,  10 milli)
    assert(listOfTrys(0).failed.get.getMessage.contains("bad news"))
    assert(listOfTrys.size == 1)
  }
  test("Future.allSucceededAsTrys returns the succeeded item and only that item") {
    var future1 = Future{"dog"}
    val future2 =  Future{throw new IllegalStateException("bad news")}

    val futureListOfTrys =  Future.allSucceededAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys,  10 milli)
    assert(listOfTrys(0) == Success("dog"))
    assert(listOfTrys.size == 1)
  }

7

আমি এই প্রশ্নটি সবেমাত্র এসেছি এবং অফারের আরও একটি সমাধান রয়েছে:

def allSuccessful[A, M[X] <: TraversableOnce[X]](in: M[Future[A]])
                                                (implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], 
                                                 executor: ExecutionContext): Future[M[A]] = {
    in.foldLeft(Future.successful(cbf(in))) {
      (fr, fa)(for (r ← fr; a ← fa) yield r += a) fallbackTo fr
    } map (_.result())
}

এখানে ধারণাটি হ'ল ভাঁজটির মধ্যে আপনি তালিকার পরবর্তী উপাদানটি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করছেন (বোঝার জন্য সিনট্যাক্স ব্যবহার করে) এবং যদি পরেরটি ব্যর্থ হয় তবে আপনার কাছে ইতিমধ্যে যা রয়েছে তার পিছনে ফ্যালব্যাক করুন।


আমি নামটি অপছন্দ করি তবে আমি এটির
পদ্ধতিটি

1

আপনি সহজেই ভবিষ্যতের ফলাফলটি বিকল্পের সাথে আবৃত করতে পারেন এবং তারপরে তালিকাটি সমতল করতে পারেন:

def futureToFutureOption[T](f: Future[T]): Future[Option[T]] =
    f.map(Some(_)).recover {
      case e => None
    }
val listOfFutureOptions = listOfFutures.map(futureToFutureOption(_))

val futureListOfOptions = Future.sequence(listOfFutureOptions)

val futureListOfSuccesses = futureListOfOptions.flatten

প্রথম ফাংশনে কারও কারও সাথে যদি ত্রুটির মুখোমুখি হয় তবে সংকলক ত্রুটি রোধ করার জন্য প্রথম ফাংশনটি এমনভাবে আবারও লেখা যেতে পারে: ডিফ ফিউচারটফিউশনঅપ્শন [টি] (চ: ভবিষ্যত [টি]): ভবিষ্যত [বিকল্প [টি]] = f.map (বিকল্প (_))। পুনরুদ্ধার করুন {কেস ই => কিছুই নয়}
জি

0

আপনি বিভিন্ন তালিকায় সফল এবং অসফল ফলাফল সংগ্রহ করতে পারেন:

def safeSequence[A](futures: List[Future[A]]): Future[(List[Throwable], List[A])] = {
  futures.foldLeft(Future.successful((List.empty[Throwable], List.empty[A]))) { (flist, future) =>
    flist.flatMap { case (elist, alist) =>
      future
        .map { success => (elist, alist :+ success) }
        .recover { case error: Throwable => (elist :+ error, alist) }
    }
  }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.