Once I decided to start using futures, they started to bleed into function return-type signatures everywhere. One place in particular that they started to show up was in the service layer:

Listing 1 trait MyService { def doSomething(i: Int) : Future[Unit] } class MyServiceImpl extends MyService { def doSomething(i: Int) : Future[Unit] = Future { Thread.sleep(500);println(i) } } 1 2 3 4 5 6 trait MyService { def doSomething ( i : Int ) : Future [ Unit ] } class MyServiceImpl extends MyService { def doSomething ( i : Int ) : Future [ Unit ] = Future { Thread . sleep ( 500 ) ; println ( i ) } }

Using a service layer like this was straightforward, until I needed to call it N times:

Listing 2 val svc : MyService = new MyServiceImpl val someInts : List[Int] = (1 to 20).toList val result : Unit = someInts.foreach(svc.doSomething _) 1 2 3 val svc : MyService = new MyServiceImpl val someInts : List [ Int ] = ( 1 to 20 ) . toList val result : Unit = someInts . foreach ( svc . doSomething _ )

This was my first intuition about how to call the service N times. It seemed straightforward to me at the time and it even compiles and runs! But what this actually does is to immediately create a List[Future[Unit]] of 20 “hot” futures — all 20 futures have been submitted for execution! While this might be ok for 20 futures, it’s not ok for 1,000, 10,000 or 100,000+ futures.

Internally, the executor stores futures in a queue and executes as many futures as it has workers simulatenously. Dumping too many futures into the executor queue at once will starve other code that uses the same executor and can cause an out of memory error. Definitely not what I wanted.

Also, there is another problem here: each future returned by svc.doSomething is discarded by assignment to Unit. Not only am I not properly waiting on my futures to complete, but by assigning my Future to Unit, I’m throwing away any exception that might be thrown! Also not what I want.