---
title: Deferred execution
layout: gem-single
name: dry-effects
---

`Defer` adds three methods for working with deferred code execution:

- `defer` accepts a block and executes it (potentially) on a thread pool. It returns an object that can be awaited with `wait`. These objects are `Promise`s made by `concurrent-ruby`. You can use their API, but it's not fully supported and tested in conjunction with effects.
- `wait` accepts a promise or an array of promises returned by `defer` and returns their values. The method blocks the current thread until all values are available.
- `later` postpones block execution until the handler is finished (see examples below).

### Defer

A simple example:

```ruby
class CreateUser
  include Dry::Effects.Resolve(:user_repo, :send_invitation)
  include Dry::Effects.Defer

  def call(values)
    user = user_repo.create(values)
    defer { send_invitation.(user) }
    user
  end
end
```

In the code above, `send_invitation` is run on a thread pool. It's the simplest way to run code concurrently.

Code within the `defer` block can use some effects but not all of them. For instance, `Interrupt` is not supported because you cannot return from one thread to another. This is a limitation of Ruby and threads in general.

### Handling

The default handler uses `concurrent-ruby` to do the heavy lifting. As an option, it accepts the executor—usually a thread pool where the code will be run.

> Three special values are also supported: `:io` returns the global pool for long, blocking (IO) tasks, `:fast` returns the global pool for short, fast operations, and `:immediate` returns the global ImmediateExecutor object.

By default, `Dry::Effects::Handler.Defer` uses `:io`.

```ruby
class HandleDefer
  include Dry::Effects::Handler.Defer(executor: :immediate)

  def initialize(app)
    @app = app
  end

  def call(env)
    # defer tasks in @app will be run on the same thread
    with_defer { @app.(env) }
  end
end
```

The executor can be passed directly to `with_defer`:

```ruby
def call(env)
  with_defer(executor: :fast) { @app.(env) }
end
```

### Using null executor

For skipping deferred tasks, create a mocked executor

```ruby
require 'concurrent/executor/executor_service'

NullExecutor = Object.new.extend(Concurrent::ExecutorService).tap do |null|
  def null.post
  end
end
```

and provide it in middleware

```ruby
class HandleDefer
  include Dry::Effects::Handler.Defer
  include Dry::Effects::Handler.Env(:environment)

  def initialize(app)
    @app = app
  end

  def call(env)
    with_defer(executor: executor) { @app.(env) }
  end

  def executor
    environment.equal?(:test) ? NullExecutor : :io
  end
end
```

### Later

`later` doesn't return a result that can be awaited. Instead, it starts deferred blocks on handler exit. Consider this example:

```ruby
class CreateUser
  def call(values)
    user_repo.transaction do
      user = user_repo.create(values)
      defer { send_invitation.(user) }
      user_account = account_repo.create(user)
      user
    end
  end
end
```

There is no guarantee `send_invitation` will be run _after_ the transaction finishes. It may lead to race conditions or anomalies. If `account_repo.create` fails with an exception, the transaction will be rolled back yet the invitation will be sent!

`later` captures the block but doesn't run it:

```ruby
later { send_invitation.(user) }
```

The invitaition will be sent when `with_defer` exits:

```ruby
with_defer { @app.(env) }
```

It usually happens outside of any transaction so that anomalies don't occur.