Exceptions and Futures in Zio

Reading Time: 3 minutes

ZIO is an open-source library that delivers a better “Future” for asynchronous and concurrent programming in Scala. Similarly, we can’t retry a Future in the event of failure, like we can for ZIO, because a Future isn’t a blueprint for doing something—it’s an executing computation. So if a Future fails, there is nothing else to do. We can only retrieve the failure. The central “type that represents action” in ZIO zio.ZIO.

Throwing Exceptions

Our code might throw exceptions. Sometimes we documented those exceptions, and sometimes we even don’t know if the code will throw exceptions or not. Throwing exceptions breaks totality, because your function is not returning a value for every possible input.

In ZIO, we should not throw exceptions within IO.succeed or map/flatMap, because it will result in the fiber being killed. Instead, we should use IO.effect. This effect constructor will catch exceptions for us and return an IO[Throwable, A] also known as Task. We can use mapErrorcatchAll or other combinators to deal with this exception.

import java.nio.file.{ Files, Paths }

import zio.IO

def copyDocument(path: String, destination: String): IO[Throwable, Unit] = 

IO.effect {

  Files.copy(Paths.get(path), Paths.get(destination))


It is a good way to handle our code when do not know that our code might throw exceptions. As we transform our partial code into an effect that explicitly states that it might fail with an exception.

Blocking the current thread

Code may not only throws exceptions but also blocks the current thread until completion? If you run it on a regular IO, you will block a thread from your application’s main thread pool and potentially cause thread starvation. Instead, we have to run such a task inside another thread pool dedicated to blocking tasks.

ZIO has a function effectBlocking in which we wrap your code within. effectBlocking will ensure that the code will run into a dedicated thread pool. The return type effectBlocking is ZIO[Blocking, Throwable, A], that means it requires a “blocking environment” (= the thread pool to use) and that it catches exceptions.

import scala.io.{ Codec, Source }

import zio.{ App, console, ZIO }

import zio.blocking._

object SampleApp extends App {

  def getResource(path: String): ZIO[Blocking, Throwable, String] = 

effectBlocking {




Future can be converted into a ZIO effect using ZIO.fromFuture:

import scala.concurrent.Future

lazy val future = Future.successful("Hello!")

val zfuture: Task[String] =

  ZIO.fromFuture { implicit ec =>

    future.map(_ => "Goodbye!")


The function passed to fromFuture is passed an ExecutionContext, which allows ZIO to manage where the Future runs (of course, you can ignore this ExecutionContext).

The error type of the resulting effect will always be Throwable, because Future can only fail with values of type Throwable.

 By wrapping function in effectAsync gives you a function. That you can call when the callback is triggered, and that will complete the effect with either a failure or a value.

 Reasons to build your programs using IO

  • Asynchronicity. Like Scala’s own FutureIO lets you easily write asynchronous code without blocking or callbacks. Compared to FutureIO has significantly better performance and cleaner, more expressive, and more composable semantics.
  • Resource SafetyIO provides composable resource-safe primitives that ensure resources like threads, sockets, and file handles are not leaking, which allows you to build long-running, robust applications. These applications will not leak resources, even in the presence of errors or interruption.
  • ConcurrencyIO has all the concurrency features, and more, based on a clean fiber concurrency model designed to scale well past the limits of native threads. Unlike other effect monads, IO‘s concurrency primitives do not leak threads.

Written by 

Gulshan Singh is a Software Consultant at Knoldus Inc. Software Developers are forever students. He is passionate about Programming.

Leave a Reply