Actors and async data

Motoko provides an actor-based programming model to developers to express server behavior, including that of micro services on the Internet and the Internet Computer.

An actor is similar to an object, but is special in that its isolated state exists remotely, and its interactions with the world are asynchronous.

Each Motoko actor represents a service that one might want to deploy on the Internet Computer.

The interface of each actor introduces async data whenever it returns information to its caller. This programming abstraction serves a key role in Motoko, as it coordinates with the transformations of the Motoko compiler pipeline and eventual execution behavior of Motoko actors on the Internet Computer.

This abstraction represents a promise from the system to the caller, on behalf of the callee:

  • Either the async value, when awaited, will yield a value from the callee of the expected type,

  • or, an error --- system-level or callee-level --- will eventually arise.

In general, the caller may not immediately await each call. But even in cases when they do, they use the same async and await abstractions, for the same reason: To maintain the illusion of call-return, direct-style control flow, as supported by the Motoko compiler’s transformations.

Technical aside. In reality, the underlying message-passing of the system forces the program’s logic into another form. Specifically, control flow around each actor method call involves the program loosing control to a system-level message-processing loop, which forces the program’s logic into a so-called "continuation-passing-style" (CPS) to expose event-handling "callback functions". This program structure is complex for humans to read and maintain, and stands in stark contrast to the direct style most prefer for most program logic.

We note that Motoko programs may avoid callbacks for many cases, but not all cases where they are used in other asynchronous, message-passing settings. Notably, callbacks are still needed when they serve as a fundamental aspect of the service’s interface, as with a notification service, where users register with the service to get notified some times later, when some predetermined class of events, occur over time.


To start, we consider the simplest stateful service: A counter with a single "current count" value.

Example: a Counter service

Consider the following actor object (a value form):

actor Counter {
  var count : Nat = 0;

  public func increment() : async () {
    count += 1;
  };

  public query func get_current() : async Nat {
    count
  };

  public func set_current(n: Nat) : async () {
    count := n;
  };
}

Using async to await values

To get the underlying content of an async value, such as a return value from get_current above, the caller uses await:

let a : async Nat = counter.get_current()
let c : Nat = await(counter.get_current())

The first line gets a promise of the current value (the variable a), but does not wait for it, and thus cannot use it as a natural number.

The second line immediately inspects this promise and gets the natural number, or waits until it is ready.

For now, the Motoko compiler gives an error for calls that do not follow this second form, which is currently required to ensure that certain program resources will always be reclaimed.

Actor classes generalize an actor’s initial state

An actor class defines a constructor function that produces objects of a predetermined type, with a predetermined interface and behavior.

For example, we can generalize Counter given above to CounterInit below, by introducing a constructor parameter, variable init of type Nat:

actor class CounterInit(init: Nat) {
  var count : Nat = init;

  public func increment() : async () {
    count += 1;
  };

  public query func get_current() : async Nat {
    count
  };

  public func set_current(n: Nat) : async () {
    count := n;
  };
}

To use this class, we can create several actors with different initial values:

let c1 = CounterInit(1);
let c2 = CounterInit(2);

The two lines above instantiate the actor class twice, once per line. The first invocation uses the initial value 1, where the second uses initial value 2. Their interface is common, and in terms of their types, they are compatible and can be used interchangeably.

For now, the Motoko compiler gives an error when compiling programs that do not consist of a single actor. The interpreter accommodates the examples above.