Documentation

You are viewing the documentation for the 3.0.4 release. The latest stable release series is 3.0.x.

§Scheduling asynchronous tasks

You can schedule sending messages to actors and executing tasks (functions or Runnable instances). You will get a Cancellable back that you can call cancel on to cancel the execution of the scheduled operation.

For example, to send a message to the testActor every 30 seconds:

Scala
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks

import javax.inject.Inject
import javax.inject.Named

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext

import org.apache.pekko.actor.ActorRef
import org.apache.pekko.actor.ActorSystem

class MyActorTask @Inject() (actorSystem: ActorSystem, @Named("some-actor") someActor: ActorRef)(
    implicit executionContext: ExecutionContext
) {
  actorSystem.scheduler.scheduleAtFixedRate(
    initialDelay = 0.microseconds,
    interval = 30.seconds,
    receiver = someActor,
    message = "tick"
  )
}
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks;

import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.pekko.actor.ActorRef;
import org.apache.pekko.actor.ActorSystem;
import scala.concurrent.ExecutionContext;
import scala.concurrent.duration.Duration;

public class MyActorTask {

  private final ActorRef someActor;
  private final ActorSystem actorSystem;
  private final ExecutionContext executionContext;

  @Inject
  public MyActorTask(
      @Named("some-actor") ActorRef someActor,
      ActorSystem actorSystem,
      ExecutionContext executionContext) {
    this.someActor = someActor;
    this.actorSystem = actorSystem;
    this.executionContext = executionContext;

    this.initialize();
  }

  private void initialize() {
    actorSystem
        .scheduler()
        .scheduleAtFixedRate(
            Duration.create(0, TimeUnit.SECONDS), // initialDelay
            Duration.create(30, TimeUnit.SECONDS), // interval
            someActor,
            "tick", // message,
            executionContext,
            ActorRef.noSender());
  }
}

Note: See Scala or Java documentation about how to inject actors.

Similarly, to run a block of code 10 seconds from now, every minute:

Scala
class CodeBlockTask @Inject() (actorSystem: ActorSystem)(implicit executionContext: ExecutionContext) {
  actorSystem.scheduler.scheduleAtFixedRate(initialDelay = 10.seconds, interval = 1.minute) { () =>
    // the block of code that will be executed
    actorSystem.log.info("Executing something...")
  }
}
Java
public class CodeBlockTask {

  private final ActorSystem actorSystem;
  private final ExecutionContext executionContext;

  @Inject
  public CodeBlockTask(ActorSystem actorSystem, ExecutionContext executionContext) {
    this.actorSystem = actorSystem;
    this.executionContext = executionContext;

    this.initialize();
  }

  private void initialize() {
    this.actorSystem
        .scheduler()
        .scheduleAtFixedRate(
            Duration.create(10, TimeUnit.SECONDS), // initialDelay
            Duration.create(1, TimeUnit.MINUTES), // interval
            () -> actorSystem.log().info("Running block of code"),
            this.executionContext);
  }
}

Or to run a block of code once 10 seconds from now:

Scala
class ScheduleOnceTask @Inject() (actorSystem: ActorSystem)(implicit executionContext: ExecutionContext) {
  actorSystem.scheduler.scheduleOnce(delay = 10.seconds) { () =>
    // the block of code that will be executed
    actorSystem.log.info("Executing something...")
  }
}
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */


  private final ActorSystem actorSystem;
  private final ExecutionContext executionContext;

  @Inject
  public CodeBlockOnceTask(ActorSystem actorSystem, ExecutionContext executionContext) {
    this.actorSystem = actorSystem;
    this.executionContext = executionContext;

    this.initialize();
  }

  private void initialize() {
    this.actorSystem
        .scheduler()
        .scheduleOnce(
            Duration.create(10, TimeUnit.SECONDS), // delay
            () -> System.out.println("Running just once."),
            this.executionContext);
  }
}

You can see the Pekko documentation to see other possible uses of the scheduler. See the documentation for pekko.actor.Scheduler for Scala or for Java.

Note: Instead of using the default ExecutionContext, you can instead create a CustomExecutionContext. See documentation for Java or Scala. See the section about it below.

§Starting tasks when your app starts

After defining the tasks as described above, you need to initialize them when your application starts.

§Using Guice Dependency Injection

When using Guice Dependency Injection, you will need to create and enable a module to load the tasks as eager singletons:

Scala
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks

import play.api.inject._
import play.api.inject.SimpleModule

class TasksModule extends SimpleModule(bind[MyActorTask].toSelf.eagerly())
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks;

import com.google.inject.AbstractModule;

public class TasksModule extends AbstractModule {

  @Override
  protected void configure() {
    bind(MyActorTask.class).asEagerSingleton();
  }
}

And then enable the module in your application.conf by adding the following line:

play.modules.enabled += "tasks.TasksModule"

As the task definitions are completely integrated with the Dependency Injection framework, you can also inject any necessary component inside of them. For more details about how to use Guice Dependency Injection, see Scala or Java documentation.

§Using compile-time Dependency Injection

When using compile-time Dependency Injection, you just need to start them in your implementation of BuiltInComponents:

Scala
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks

import play.api.routing.Router
import play.api.ApplicationLoader.Context
import play.api.BuiltInComponentsFromContext
import play.api.NoHttpFiltersComponents

class MyBuiltInComponentsFromContext(context: Context)
    extends BuiltInComponentsFromContext(context)
    with NoHttpFiltersComponents {
  override def router: Router = Router.empty

  // Task is initialize here
  initialize()

  private def initialize(): Unit = {
    new CodeBlockTask(actorSystem)
  }
}
Java
/*
 * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
 */

package tasks;

import play.ApplicationLoader;
import play.BuiltInComponentsFromContext;
import play.filters.components.NoHttpFiltersComponents;
import play.routing.Router;

public class MyBuiltInComponentsFromContext extends BuiltInComponentsFromContext
    implements NoHttpFiltersComponents {

  public MyBuiltInComponentsFromContext(ApplicationLoader.Context context) {
    super(context);

    this.initialize();
  }

  private void initialize() {
    // Task is initialize here
    new CodeBlockTask(actorSystem(), executionContext());
  }

  @Override
  public Router router() {
    return Router.empty();
  }
}

This must then be used with your custom ApplicationLoader implementation. For more details about how to use compile-time Dependency Injection, see Scala or Java documentation.

§Using a CustomExecutionContext

You should use a custom execution context when creating tasks that do sync/blocking work. For example, if your task is accessing a database using JDBC, it is doing blocking I/O. If you use the default execution context, your tasks will then block threads that are using to receive and handle requests. To avoid that, you should provide a custom execution context:

Scala
import javax.inject.Inject

import org.apache.pekko.actor.ActorSystem
import play.api.libs.concurrent.CustomExecutionContext

class TasksCustomExecutionContext @Inject() (actorSystem: ActorSystem)
    extends CustomExecutionContext(actorSystem, "tasks-dispatcher")
Java
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.apache.pekko.actor.ActorSystem;
import play.libs.concurrent.CustomExecutionContext;
import scala.concurrent.duration.Duration;

public class TasksCustomExecutionContext extends CustomExecutionContext {

  @Inject
  public TasksCustomExecutionContext(ActorSystem actorSystem) {
    super(actorSystem, "tasks-dispatcher");
  }
}

Configure the thread pool as described in thread pools documentation using tasks-dispatcher as the thread pool name, and then inject it in your tasks:

Scala
class SomeTask @Inject() (actorSystem: ActorSystem, executor: TasksCustomExecutionContext) {
  actorSystem.scheduler.scheduleAtFixedRate(initialDelay = 10.seconds, interval = 1.minute) { () =>
    actorSystem.log.info("Executing something...")
  }(executor) // using the custom execution context
}
Java
public class SomeTask

  private final ActorSystem actorSystem;
  private final TasksCustomExecutionContext executor;

  @Inject
  public SomeTask(ActorSystem actorSystem, TasksCustomExecutionContext executor) {
    this.actorSystem = actorSystem;
    this.executor = executor;

    this.initialize();
  }

  private void initialize() {
    this.actorSystem
        .scheduler()
        .scheduleAtFixedRate(
            Duration.create(10, TimeUnit.SECONDS), // initialDelay
            Duration.create(1, TimeUnit.MINUTES), // interval
            () -> actorSystem.log().info("Running block of code"),
            this.executor // using the custom executor
            );
  }
}

§Use third party modules

There are also modules that you can use to schedule tasks. Visit our module directory page to see a list of available modules.

Next: Application Shutdown