åºæ¬çã«ä»¥ä¸ãããã®ã¾ã¾åã£ã¦ãã¦ãã¾ãã
- ããã¥ã¡ã³ã Futures — Util 21.5.0 documentation
- ã³ã¡ã³ã util/Future.scala at util-6.45.0 · twitter/util · GitHub
説æç¨ã®ãã£ãããã¤ã³ã¿ã¨ãã¦ã¾ã¨ããäºå®ã ã£ãã®ã«ãããããããã¨æ¬²å¼µã£ã¦ãã¾ã£ã代ç©ãï¼ãã®å²ã«å ¨é¨ããããã§ã¯ãªããï¼ playããfinagleã«ç§»è¡ãã¦ãããããã¨ã¡ã½ããåãç´°ããéã£ããããã®ã«æåã¯æ¸æãã¾ãããScalaæ¨æºFutureã¨akkaã®schedulerã使ã£ããã¨ãããã°ãããæ £ãã¤ã¤ä¾¿å©ããæããããã¨æãã¾ãã
ä¾ã§ã¯Thread.sleepãå¼ã³ã¾ãã£ã¦ãã¾ããããã®ã¸ãã®äºæ ã¯Scalaæ¨æºã®Futureã¨åããªã®ã§ã¡ããã¨ããã¨ãã¯Future.sleepãtimer.doLaterãªã©ã使ã£ãã»ããè¯ãã§ãã
ç®æ¬¡
- ç®æ¬¡
- Futureã®ä½æ
- Futureãã¾ã¨ãã
- 失æã®ãã³ããªã³ã°
- FuturePool
- Timerç³»å
- è¤æ°ã®å¦ç
- 便å©const
- callback
- ãã£ã³ã»ã«
- monitored
- ãã¾ã: Futureã®å帰
Futureã®ä½æ
åºæ¬çãªä½ãæ¹ã«ã¤ãã¦ã ãã®ä½ãæ¹ã ã¨applyãå«ãã¦å ¨é¨åæå®è¡ã«ãªãç¹ã«æ³¨æã
value/exception
import com.twitter.util.Future // åºæ¬ã®ä½ãæ¹ Future.value(1) Future.exception(new RuntimeException)
apply/const
Scalaæ¨æºFutureã§ã¯applyã¯éåæã«å®è¡ãããããTwitterFutureã§ã®applyã¯åæå®è¡ã¨ããéãã«æ³¨æã
import com.twitter.util.Future // applyã使ãã¨Tryã§å ãã®ã§Return/Throwã«ä»åãã¦ããã // Scalaæ¨æºã®Futureã¨ç°ãªãããã Tryã§å ãã ã == åæå®è¡ãªç¹ã«æ³¨æã Future(1) Future(throw new RuntimeException) // twitter Tryããã®å¤æ Future.const(Try(1)) Future.const(Try(throw new RuntimeException))
Futureãã¾ã¨ãã
Scalaæ¨æºã®Futureã¨åããforå¼ã使ãã ãã ã¨ä¸¦è¡å®è¡ãããªããã¨ãããã®ã§æ³¨æã
map/flatMap
ç¥
join
ScalaFutureã®zipã¨åã http://qiita.com/mtoyoshi/items/f68beb17710c3819697f#zip ã³ã¼ãä¾ã¯ç¥ã
失æã®ãã³ããªã³ã°
handle/rescue
import com.twitter.util.{Future, Return, Throw} val successFuture = Future.value(1) val failedFuture = Future.exception[Int](new RuntimeException) // handle // 失æããä¾å¤ãSuccessã«ã§ããã failedã«å¯¾ããmap // caseã¯PartialFunctionãªã®ã§caseã«ãããããªãä¾å¤ã¯ãã®ã¾ã¾ä¾å¤ã¨ãã¦æ±ãããã successFuture.handle { case e: RuntimeException => 0 } failedFuture.handle { case e: RuntimeException => 0 } // rescue // 失æããä¾å¤ãSuccessã«ãããå¥ã®ä¾å¤ã«å¤æã§ããã failedã«å¯¾ããflatMap // caseã¯PartialFunctionãªã®ã§caseã«ãããããªãä¾å¤ã¯ãã®ã¾ã¾ä¾å¤ã¨ãã¦æ±ãããã successFuture.rescue { case e: RuntimeException => Future.value(0) } failedFuture.rescue { case e: RuntimeException => Future.value(0) }
transform
import com.twitter.util.{Future, Return, Throw} val successFuture = Future.value(1) val failedFuture = Future.exception[Int](new RuntimeException) // transform // rescueã¨ç°ãªãæåæã®å¤ãåæã«å¤æã§ãã successFuture.transform { case Return(a) if a == 1 => Future.exception(new RuntimeException) case Return(a) => Future.value(a) case Throw(e: RuntimeException) => Future.value(0) case Throw(e) => Future.exception(e) } failedFuture.transform { case Return(a) => Future.value(a) case Throw(e: RuntimeException) => Future.value(0) case Throw(e) => Future.exception(e) } // transformedByã¨ããã¡ã½ããããããããã¡ãã¯FutureTransformerãåãåãã // FutureTransformerã¯JavaFriendlyã¨æ¸ãã¦ããã®ã§åºæ¬çã«ã¯transformã使ãã°è¯ãã // ä»åã¯å²æã
FuturePool
éåæå®è¡ãããå ´åã¯FuturePoolã®åãå¿ è¦ã
FuturePool.unboundedPool
ã¹ã¬ãããã¼ã«å é¨ã®ExecutorServiceã¯globalã®ãã®(https://github.com/twitter/util/blob/util-6.45.0/util-core/src/main/scala/com/twitter/util/FuturePool.scala#L70-L72) ãå©ç¨ãããã
import com.twitter.util.FuturePool // éåæå®è¡ããããã¼ã« // å¦çã®å®è¡æ¹æ³ã¯Future.applyã¨åããpoolã®applyã«å¦çã渡ãã°OK // unboundedãªã®ã§ééãªãæ¡å¼µãããã val unboundedPool = FuturePool.unboundedPool unboundedPool(1) unboundedPool(throw new RuntimeException)
FuturePool(dbExecutorService).apply
import java.util.concurrent.ForkJoinPool import com.twitter.util.FuturePool // éåæå®è¡ããããã¼ã«ãèªåã§ä½ãã val dbExecutorService = new ForkJoinPool(50) val myPool = FuturePool(dbExecutorService) myPool { Thread.sleep(1); 1 } myPool(throw new RuntimeException) // interruptibleUnboundedPoolã¨ãããã£ã³ã»ã«ã«å¯¾å¿ããPoolãããã
Timerç³»å
Timerã®ç¨®é¡ãä¾ãã¨ã«å¤ãã¦ããã使ãæ¹ã¯ã©ããåããTimerã®ç¨®é¡ã«ã¤ãã¦ã¯å¾è¿°ã
sleep
import com.twitter.util.{Future, Await} import com.twitter.conversions.time._ implicit val timer = com.twitter.finagle.util.DefaultTimer // 3ç§å¾ã«Unitãè¿ã val f = Future.sleep(3.seconds) Await.result(f)
delayed
å®äºãé ããã ãã§è¨ç®èªä½ã¯ããè¡ãããç¹ã«æ³¨æã
import com.twitter.util.{Future, Await} import com.twitter.conversions.time._ implicit val timer = new com.twitter.util.JavaTimer(isDaemon = true) // 3ç§å¾ã«Intãè¿ã val f = Future { println("in future") 1 }.delayed(3.seconds).foreach(_ => println("done")) Await.result(f)
Timer#schedule
Futureã®ã¡ã½ããã§ã¯ãªããã¤ãã§ãªã®ã§ç´¹ä»ã
import com.twitter.util.{Await, Future, Time} import com.twitter.conversions.time._ implicit val timer = new com.twitter.util.ScheduledThreadPoolTimer(poolSize = 5, makeDaemons = true) // schedule 1ç§ãã¨ã«ä½åº¦ãå®è¡ãããããã£ã³ã»ã«å¯è½ãªTimerTaskãè¿ãã val timerTask = timer.schedule(1.seconds) { println("1sec!") } Thread.sleep(3000) Await.result(timerTask.close()) // doLater 2ç§å¾ã«1åå®è¡ãããFutureãè¿ãã val f1 = timer.doLater(2.seconds) { println("2sec!") } Await.result(f1) // doAt å ·ä½çãªæå»ãæå®ãããFutureãè¿ãã val f2 = timer.doAt(Time.Now + 3.seconds) { println("3sec!") } Await.result(f2)
timerã®è©±
tl;dr;
- finagleã使ã£ã¦ãããªãcom.twitter.finagle.util.DefaultTimerã使ãã°OKã ããblockingãªéãå¦çããããªãèªåã§å®ç¾©ããã»ããããããã
- finagle-netty4ãä¾åãã¹ã«ããã°Netty4HashedWheelTimerã使ãããã
- ç¡ãã¦ãJavaTimer(isDaemon=true)ã使ãããã®ã§å®å¿ã
- finagleã¯ä½¿ã£ã¦ç¡ãã¦twitter/utilã ã使ã£ã¦ããå ´åã¯JavaTimerãScheduledThreadPoolTimerãé¸æè¢
- åã«
new JavaTimer
ã¨ããã¨ã¦ã¼ã¶ã¼ã¹ã¬ããã¨ãã¦èµ·åãããããæ示çã«cancelãå¼ã°ãªãã¨timerãGCãããã¾ã§ããã»ã¹ãçµäºããªããªããã¨ãããã®ã§isDaemon=trueãæå®ããã¨ããã
- åã«
ç´°ãã話
twitter/utilã®ãã®æã®ã¡ã½ããã¯com.twitter.util.Timer
ãè¦æ±ãã¦ããã
ã¹ã±ã¸ã¥ã¼ãªã³ã°ãç¡è¦ãã¦å³æå®è¡ããNullTimerããã¹ãç¨ã®MockTimerããããã finagleã使ã£ã¦ããªãå ´åãåºæ¬çã«ã¯JavaTimer(isDaemon=true)ããããç¨åº¦ã®æ§è½ã欲ããå ´åã¯ScheduledThreadPoolTimerã使ãã°è¯ãããã
finagleã使ã£ã¦ããå ´åã¯ãããå°ãæ§è½ãåºãã¿ã¤ãã¼ãDefaultTimerã¨ãã¦finagleèªä½ã«ç¨æããã¦ããã®ã§ãã¡ãã使ãæ¹ãè¯ãããã ãã ãDefaultTimerã®ã¤ã³ã¹ã¿ã³ã¹ã¯å ±éãªã®ã§blockingãªå¦çãè¡ãéã¯ããã®ã¹ã¬ããããããã¯ãããå¯è½æ§ãããã®ã§åããã»ããè¯ããããããªããï¼æªæ¤è¨¼ï¼
com.twitter.finagle.util.DefaultTimer
ã®å®è£
ã¯ServiceLoaderã§ä¸çªæåã«è¦ã¤ãã£ãã¯ã©ã¹ã使ãã
ServiceLoaderã§è¦ã¤ãããªãã£ãå ´åã¯warningãã°ãåºã¤ã¤com.twitter.util.JavaTimer
ãdaemonThreadãONã«ããä¸ã§ä½¿ãããã«ãªã£ã¦ããã
https://github.com/twitter/finagle/blob/finagle-6.45.0/finagle-core/src/main/scala/com/twitter/finagle/util/DefaultTimer.scala#L31-L36
finagle-netty4(finagle-{thrift, http}ã®ä¾åã«ãã)ãNetty4HashedWheelTimerãæå®ãã¦ããã®ã§å¤§æµã®å ´åã¯ãããèªã¿è¾¼ã¾ããæ°ããããã https://github.com/twitter/finagle/blob/finagle-6.45.0/finagle-netty4/src/main/resources/META-INF/services/com.twitter.finagle.util.DefaultTimer
ããä¸ã¤com.twitter.finagle.util.HashedWheelTimer.Default
ã¨ããããããããã®ãåå¨ãããããã¡ãã¯Netty3ãã¼ã¹ã®HashedWheelTimerã使ã£ã¦ããã
Netty3,4éã®HashedWheelTimerã®å·®ã¯ããããã£ã¦ããªããæ°ããæ¹ãè¯ããããªã®ã§åºæ¬çã«ã¯com.twitter.finagle.util.DefaultTimerã使ãã®ãè¯ãã ããã
Timerã®ãã¼ãã«ãµã¼ãã¹ãã¼ãã¼ã使ãããã«ãªã£ãã®ã¯finagle6.45ãããçµç·¯ã¨ã㯠https://github.com/twitter/finagle/commit/d047b4568e07a56b481b5f7c193b0e8c5ec6ff71 ã®ã³ãããã«æ¸ãã¦ããéããfinagle-coreããnetty3ä¾åãå¥ããããã«ãããªã£ã¦ãããããã
è¤æ°ã®å¦ç
select/or
import com.twitter.util.{Future, Try} // selectã¯ä¸çªæåã«çµãã£ãFutureã®å¤ã¨æ®ãã®Futureãè¿ãã val fs = (1 to 5).map(Future(_)) val (firstDoneTry, others): (Try[Int], Seq[Future[Int]]) = Await.result(Future.select(fs)) println(firstDoneTry) // Return(1) println(others) // Seq(Future(Return(2)), Future(Return(3)), Future(Return(4)), Future(Return(5))) // orã¯selectã®ï¼ã¤çãselectã¨ç°ãªããå ã«çµãã£ãå¤ãå«ã¾ããFutureã®ã¿ãè¿ãã Future(1).or(Future(2)) // selectIndexã¨ããä¸çªæåã«çµãã£ãSeq[Future[A]]ã®indexãè¿ãã¡ã½ããããããå²æã
traverseSequentially/collect/collectToTry
traverseSequentiallyã¯åã®Futureãçµäºãã¦ãã次ãå®è¡ããcollectã¯åæã«å®è¡ããã
å®è£ ãè¦ãã¨collectã§ãçµæã«å«ã¾ããè¦ç´ ã®é çªã¯å¼æ°ã¨åãã«ãªãã£ã½ããé åºã«ã¤ãã¦ã¯ããã¥ã¡ã³ããã³ã¡ã³ãã«ã¯ã®ã£ã¦ãªãæ°ãããã
collectToTryã¯collectã¨ç°ãªãä¸é¨ã失æããã¨ããæåããä¸é£ã®çµæãTryã§åå¾ã§ããã
import com.twitter.util.{Await, Future} // é çªã«å®è¡ããã val f = Future.traverseSequentially(1 to 5) { i => Future(i) } // 並åã«å®è¡ããã val f = Future.collect((1 to 5).map { i => Future { val wait = scala.util.Random.nextInt(300) println(s"$i => $wait ms") Thread.sleep(wait) i } }) println(Await.result(f)) // é åºã¯åããArraySeq(1, 2, 3, 4, 5) // 並åã«å®è¡ãããã失æãè£è¶³ã§ãã val f2 = Future.collectToTry((1 to 5).map { i => Future { if(i % 2 == 0) throw new RuntimeException else i } }) println(Await.result(f2)) // é åºã¯åããArraySeq(Return(1), Throw(java.lang.RuntimeException), Return(3), Throw(java.lang.RuntimeException), Return(5))
batched
大éã®éåæå¦çãããã¤ãã®ã°ã«ã¼ãã«åãã¦å®è¡ããå
åæå®è¡æ°ã®å¶éã¨ããæå³ã 㨠com.twitter.concurrent.AsyncSemaphore
ã使ãæãããã Futures — Util 21.5.0 documentation
batchedã¯è¤æ°ãªã¯ã¨ã¹ããã¾ã¨ãã¦éãã®ãåæã«ãªã£ã¦ãã(Seq[In]=> Seq[Out]ã§å¦çãè¨è¿°ãã)ãªã©ã®éããããã
import com.twitter.conversions.time._ import com.twitter.util.{Await, Future} implicit val timer = new com.twitter.util.JavaTimer(isDaemon = true) // ã¾ãbatcherãä½ã val batcher = Future.batched[Int, String]( sizeThreshold = 10, // å®è¡ãããå¤ã¯10ãæ£ç¢ºã«ã¯sizeThreshold*sizePercentileåã®ã¸ã§ããã¨ã³ãã¥ã¼ãããã¾ã§å®è¡ãå¾ ã¤ããã«ãªã£ã¦ããã timeThreshold = 500.milliseconds, // sizeã®æ¡ä»¶ãæºãããªãã¦ãenqueueããtimeThresholdãè¶ éãããã¸ã§ããå®è¡ããã sizePercentile = 0.3f // sizeThresholdã¨åããã¦æä½ã¸ã§ãå®è¡æ°ã決ããã // ãã®ä¾ã§ã¯åºå®å¤ãå ¥ãã¦ãããåå渡ãã«ãªã£ã¦ããã®ã§Random.nextFloat()ã¨ãå ¥ããã¨ããããµã¤ãºãé½åº¦å¤åã§ããããã«ãªã£ã¦ããã // å¿ è¦ããªããã°æå®ããªãã§ããã©ã«ãå¤ï¼1.0fï¼ã使ãã°è¯ãããã ) { ids => // Seq[In]ãåãåã£ã¦ãSeq[Out]ãè¿ãé¢æ°ãæ¸ã Future.sleep(scala.util.Random.nextInt(50).milliseconds).map { _ => println(s"${ids.mkString(", ")} are inserted") ids.map(_.toString) } } val userIds = 1 to 50 // applyã«æ¸¡ãã¦ã¸ã§ããenqueueãããä¸ã§synchronizedããã®ã§collectã§å¼ã¶æå³ã¯ãã¾ããªãã // ãããã°ã«ã¼ããã¨ã«sleepãå ¥ãããããæ©è½ã¯ç¡ããããªã®ã§ãenqueueèªä½ã®ã¿ã¤ãã³ã°ã§å¶å¾¡ããã¨è¯ãããã val insertedFuture = Future.collect(userIds.map(batcher)) println(Await.result(insertedFuture)) // thresholdã«é¢ä¿ãªãå ¨ãªã¯ã¨ã¹ãã®å®è¡ãéå§ãããã¨ãã¯flushBatchãå¼ã¶ã batcher.flushBatch() // sizeThreshold = 1ã§userIdsã10åæå ¥ããå ´åã®åºå // 8 are inserted // 5 are inserted // 2 are inserted // 6 are inserted // 3 are inserted // 4 are inserted // 1 are inserted // 10 are inserted // 9 are inserted // 7 are inserted // ArraySeq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // sizeThreshold = 100ã§userIdsã10åæå ¥ããå ´åã®åºå // timeThresholdç§çµéãã¦ãã以ä¸ãåºåãããã // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 are inserted // ArraySeq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // sizeThreshold = 20ã§userIdsã10åæå ¥ããå ´åã®åºå(sizePercentile=0.3fãªã®ã§20*0.3=6åãã¤å®è¡ãããã // ãã ã7~10åç®ã¯æ°ã足ããªãã®ã§timeThresholdç§çµéãã¦ããåºåãããã // 1, 2, 3, 4, 5, 6 are inserted // 7, 8, 9, 10 are inserted // ArraySeq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
便å©const
import com.twitter.util.Future // 以ä¸ã®ï¼ã¤ã¯åã Future.Unit Future.Done // 便å©å®æ° Future.True Future.False Future.None Future.Nil Future.??? // Javaç¨ãªã®ã§ä½¿ããªãã¦è¯ã Future.Void
callback
ã³ã¼ã«ããã¯ããã¯map/flatMapã§ã¤ãªããããensureã¯ããããã
onSuccess/onFailure
çç¥
respond/ensure
import com.twitter.util.Future // respond // å®äºããéã®ã³ã¼ã«ããã¯ãè¨å®ããã // 主ã«ã©ã¤ãã©ãªãªã©ã®æ±ç¨ã³ã¼ãåãã¨ã³ã¡ã³ãã«ããã // respondã¯çµæ表示ããªã½ã¼ã¹ã®å¾å§æ«ãªã©ã®å¯ä½ç¨ãèµ·ããåæã¨ãªãã(promiseã解決ããããã¦ããã³ã¼ããã¡ããã¡ããè¦ãããã) val f1 = Future.value(1) val f2 = f1.respond { case Return(a) => println(a) case Throw(e) => println(e) } // ensure // respondã¨ã»ã¼åãã ããå¼æ°ã¨ãã¦è¨ç®ã®çµæãåãåããªãã // æåãã¦ã失æãã¦ãããããåã«ãªã½ã¼ã¹ãcloseãããå ´åãªã©ã«ä½¿ããã val f1 = Future.value(1) val f2 = f1.ensure { println("f1 finished") }
ãã£ã³ã»ã«
raise
import com.twitter.util.{Await, Future, FuturePool} // Future.valueã¯å³æè©ä¾¡ãªã®ã§raiseã§ããªãã // ã¾ãinterruptibleUnboundedPoolã使ã£ã¦ããstate=Doneã«ãªãã¨raiseãå¼ãã§ãæ£å¸¸ç³»ã®å¤ãè¿ã£ã¦ããã®ã§sleepã§ãã¾ããã¦ãã val f1 = FuturePool.interruptibleUnboundedPool { Thread.sleep(100) 1 } f1.raise(new RuntimeException("interrupt")) Await.result(f) // java.util.concurrent.CancellationException // FuturePool.unboundedPoolã使ãå ´åã¯Future#interruptibleã使ãã¨interruptã§ããããã«ãªããéã«interruptibleãå¼ã°ãªãã¨cancelåºæ¥ãªãã val f2 = FuturePool.unboundedPool { Thread.sleep(100) 1 }.interruptible() f2.raise(new RuntimeException("interrupt")) Await.result(f2)
raiseWithin
Nç§ä»¥å ã«çµãããªãã¨ã¿ã¤ã ã¢ã¦ãã¨ãã£ãæå®ãåºæ¥ãã
import com.twitter.util.{Await, Future, FuturePool} import com.twitter.conversions.time._ implicit val timer = new com.twitter.util.JavaTimer(isDaemon = true) val f = FuturePool.interruptibleUnboundedPool { Thread.sleep(3000) 1 } // 2ç§å¾ã«raiseããã f.raiseWithin(2.seconds) Await.result(f)
within/by
winthinã¨byã¯DurationãåãåããTimeãåãåããã®éãããç¡ãã
raseWithinã¨within/byã«ã¯å¦çèªä½ã®futureãraiseããããwithinãªã©ã®å¼ã³åºãã®è¿ãå¤ã®ã¿ãraiseãããã®å¾®å¦ãªéããããã詳細ã¯ä»¥ä¸ã®ã³ã¡ã³ããåç §ã
import com.twitter.util.{Await, Future, FuturePool} import com.twitter.conversions.time._ implicit val timer = new com.twitter.util.JavaTimer(isDaemon = true) val f1 = FuturePool.interruptibleUnboundedPool { Thread.sleep(3000) 1 } // 2ç§å¾ã«f2ãraiseãããããraseWithinã§ã¯f1,f2ã®ä¸¡æ¹raiseãããã®ã«å¯¾ããwithin/byã¯f2ã®ã¿ãraiseãããããf1èªä½ã®çµæã¯æ®éã«åå¾ãããã¨ãåºæ¥ãã val f2 = f1.within(2.seconds) println(Await.result(f1)) // 1ã表示ããã Await.result(f2)
monitored
Promise使ãã¤ã¤ãã¹ããã¦ããã¨è¾ããªãã±ã¼ã¹ãæãããããããã¾ã®ã¨ãã使ã£ããã¨ã¯ç¡ãã
import java.util.concurrent.ForkJoinPool import com.twitter.util.{Future, FuturePool, Try, Return, Throw, Promise} import scala.util.control.NonFatal val exception = new RuntimeException("test") // 以ä¸ã®ãããªã±ã¼ã¹ãèããã¨ãnotMonitoredã¯æ±ºãã¦çµäºããªãã val inner1 = new Promise[Int] val inner2 = new Promise[Int] val notMonitored: Future[Int] = { inner1.ensure { throw exception inner2.update(Return(2)) } inner2 } // ãã®ãããªã±ã¼ã¹ãé²ãããã«å é¨ã§èµ·ããä¾å¤ãä¼æ¬ãã¦ãããã®ãFuture.monitored val monitored: Future[Int] = Future.monitored { inner1.ensure { throw exception inner2.update(Return(2)) } inner2 } // before inner1 // state=Waiting inner2 // state=Waiting notMonitored.poll // None monitored.poll // None inner1.update(Return(1)) // after inner1 // state=Done inner2 // state=Interuppted notMonitored.poll // None (æ°¸ä¹ ã«çµãããªã) monitored.poll // Some (ä¾å¤ãä¼æ¬ãããã®ã§çµãã)
ãã¾ã: Futureã®å帰
ããã¥ã¡ã³ãã«ã¡ããã¨å®è£ ãã¦ããããå帰ãã¦ããµã³ãã«ã«ãããããªã³ã¼ãã§ã¯ã¹ã¿ãã¯ãªã¼ãã¼ããã¼ã«ãªããªããã¨æ¸ãã¦ããã