Janet 1.38.0-73334f3 Documentation
(Other Versions:
1.37.1
1.36.0
1.35.0
1.34.0
1.31.0
1.29.1
1.28.0
1.27.0
1.26.0
1.25.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.0
1.19.0
1.18.1
1.17.1
1.16.1
1.15.0
1.13.1
1.12.2
1.11.1
1.10.1
1.9.1
1.8.1
1.7.0
1.6.0
1.5.1
1.5.0
1.4.0
1.3.1
)
Fiber Overview
Janet has support for single-core asynchronous programming via coroutines or fibers. Fibers allow a process to stop and resume execution later, essentially enabling multiple returns from a function. This allows many patterns such as schedules, generators, iterators, live debugging, and robust error handling. Janet's error handling is actually built on top of fibers (when an error is thrown, control is returned to the parent fiber).
A temporary return from a fiber is called a yield, and can be invoked
with the yield
function. To resume a fiber that has been
yielded, use the resume
function.
When resume
is called on a fiber, it will only return when that
fiber either returns, yields, throws an error, or otherwise emits a
signal. In all of these cases a value is produced and can be obtained
via the function fiber/last-value
. This function takes a fiber
as its sole argument. If a fiber has never been resumed,
fiber/last-value
will return `nil`.
Different from traditional coroutines, Janet's fibers implement a
signaling mechanism, which is used to distinguish between different
kinds of returns. When a fiber yields or throws an error, control is
returned to the calling fiber. The parent fiber must then check what
kind of state the fiber is in to differentiate errors from return
values from user-defined signals. This state is referred to as the
status of a fiber and can be checked using the fiber/status
function. This function takes a fiber as its sole argument.
To create a fiber, use the fiber/new
function. The fiber
constructor takes one or two arguments. The first argument (required)
is the function that the fiber will execute. This function must
accept an arity of zero. The next argument (optional) is a collection
of flags checking what kinds of signals to trap and return via
resume
. This is useful so the programmer does not need to
handle all of the different kinds of signals from a fiber. Any
untrapped signals are simply propagated to the previous calling fiber.
Note that the terms "block", "mask", and "capture" are sometimes used to refer to the "trapping" of signals.
(def f (fiber/new (fn []
(yield 1)
(yield 2)
(yield 3)
(yield 4)
5)))
# Get the status of the fiber (:alive, :dead, :debug, :new, :pending, or :user0-:user9)
(fiber/status f) # -> :new
(fiber/last-value f) # -> fiber hasn't been resumed yet, so returns nil
(resume f) # -> 1
(fiber/last-value f) # -> 1 (again)
(resume f) # -> 2
(resume f) # -> 3
(resume f) # -> 4
(fiber/status f) # -> :pending
(resume f) # -> 5
(fiber/status f) # -> :dead
(resume f) # -> throws an error because the fiber is dead
Using fibers to capture errors
Besides being used as coroutines, fibers can be used to implement error handling (i.e. exceptions).
(defn my-function-that-errors []
(print "start function")
(error "oops!")
(print "never gets here"))
# Use the :e flag to only trap errors.
(def f (fiber/new my-function-that-errors :e))
(def result (resume f))
(if (= (fiber/status f) :error)
(print "result contains the error")
(print "result contains the good result"))
Since the above code is rather verbose, Janet provides a try
macro which
is similar to try/catch in other languages. It wraps the body in a new fiber,
resume
s the fiber, and then checks the result. If the fiber has errored,
an error clause is evaluated.
(try
(error 1)
([err] (print "got error: " err)))
# evaluates to nil and prints "got error: 1"
(try
(+ 1 2 3)
([err] (print "oops")))
# Evaluates to 6 - no error thrown
The signal
function can be used to raise a particular signal.
The function's first argument, what
, specifies the signal,
while the second argument, x
, is an arbitrary payload value.
(try
(signal :error 1)
([err] (print "got error: " err)))
# evaluates to nil and prints "got error: 1"
(defer (pp :hey)
(signal :user4 :hello)
(pp :unreached))
# only prints :hey