Saltar al contenido

22 · Expresiones Temporales

Agendamiento: every, after y timeout

Bucle periódico con `every`, disparo único con `after`, y cancelación por plazo con `timeout`.

En esta página

Zolo tiene tres construcciones de agendamiento integradas en el lenguaje. Todas son palabras reservadas — no uses every, after ni timeout como nombres de variable; prefiere intervalo, agendado, plazo o limite en su lugar.

every — bucle periódico

every <dur> { ... } repite el cuerpo en el intervalo indicado. Es un bucle infinito por diseño: termínalo con break cuando se alcance la condición de salida.

Un contador con break tras 3 iteraciones; una suma acumulada que se detiene al alcanzar el total deseado.

05-every.zolo
Playground
// Feature: periodic loop with `every <dur> { ... }`

// Syntax: `every <duration> { body }` — repeats the body at the given

// interval. It is a keyword (not a variable name).

// When to use: health-checks, polling, animation, metrics, any

// task that needs to run repeatedly on a fixed interval.


var tick = 0

// Runs 3 times (short interval for the demo) and exits with `break`.

every 50ms {
  tick = tick + 1
  print("tick #{tick}")
  if tick >= 3 {
    break
  }
}

print("done")

// expected:

// tick #1

// tick #2

// tick #3

// done


// Another example: summing until a total is reached.

var total = 0
every 25ms {
  total = total + 10
  if total >= 30 {
    break
  }
}
print("total = {total}")
// expected: total = 30

after — disparo único

after <dur> { ... } agenda la ejecución del bloque una sola vez, tras el retraso. Es fire-and-forget: no bloquea el flujo principal. Usa sleep después para mantener el programa vivo hasta que el callback se dispare.

Tres after coexistiendo; el orden de disparo lo determina el scheduler.

06-after.zolo
Playground
// Feature: schedule one execution with `after <dur> { ... }`
// Syntax: `after <duration> { body }` — fire-and-forget; the block
// runs **once** after the delay. It is a keyword.
// When to use: one-shot callbacks, timers, expirations, single
// reminders. Unlike `every`, it does not repeat.

print("scheduling...")

// Block runs 50ms later — does not block the main flow here.
after 50ms {
  print("fired after 50ms!")
}

// Multiple `after` may coexist.
after 100ms {
  print("fired after 100ms!")
}

after 25ms {
  print("this one was first")
}

print("scheduled, waiting...")

// Keep the program alive long enough to see the firings.
sleep 200ms
print("end")
// expected (approximate order — depends on the scheduler):
// scheduling...
// scheduled, waiting...
// this one was first
// fired after 50ms!
// fired after 100ms!
// end

timeout — cancelación por plazo

timeout <dur> { ... } devuelve un objeto { ok, value, error }. Si el bloque concluye dentro del plazo, ok es true y value contiene el retorno. Si expira, ok es false y error es "timeout". Como el scheduler es cooperativo, la cancelación solo ocurre en puntos de yield (sleep, IO, etc.).

Camino feliz con retorno dentro del plazo; nota sobre el comportamiento cooperativo y alternativa de nombre para la variable de plazo.

07-timeout.zolo
Playground
// Feature: cancel work that takes too long with `timeout`

// Syntax: `timeout <dur> { body }` returns `Result<T, TimeoutError>`.

// When to use: network calls, uncertain IO, tasks that need a

// fail-safe. Cooperative: only fires at a yield point.


// Happy path: body finishes within the deadline.

let result_ok = timeout 100ms {
  return 42
}
match result_ok {
  Result::Ok(value) => print("ok: {value}"),
  Result::Err(error) => print("err: {error.message}"),
}

// expected: ok: 42


// IMPORTANT: `timeout` is a reserved keyword — you cannot use it

// as a variable name. Use `deadline`, `limit`, `dur`, etc. instead.

let limit = 5000
print("using '{limit}' instead of 'timeout' as the name")

// Timeout case: body takes longer than the limit and is interrupted.

// Note: cooperative — the deadline is checked each time the body

// yields (e.g. via `sleep`), so slow work needs checkpoints: a body

// that never yields, or yields once and then finishes, runs to

// completion and returns Ok. A yielding body also needs a task to

// yield to, so run the block inside `spawn` (the top-level main

// thread cannot yield); the task prints after the main chunk ends.

spawn {
  let result_slow = timeout 30ms {
    for _ in 0..10 {
      sleep 20ms  // each sleep is a checkpoint where the deadline can fire

    }
    return "too late"
  }
  match result_slow {
    Result::Ok(value) => print("ok: {value}"),
    Result::Err(error) => print("err: {error.message}"),
  }
}
// expected: err: timeout

Desafío

Combina every 100ms con un after 500ms que establezca un flag de parada. El every debe verificar el flag y llamar a break cuando sea verdadero.

Consulta también

Índice global

Encuentra tu camino por Zolo

Prueba una idea

Empieza aquí

9 resultados

9 resultados

enespt-br