Keikaku provides built-in primitives for non-blocking operations, including async protocols, promises, and deferred execution - all integrated seamlessly with the sequence/generator system.
async protocol - Define asynchronous functionsawait - Wait for promise resolutionresolve(value) - Create resolved promisedefer(ms, fn, ...args) - Delayed executionsleep(ms) - Pause executionAn async protocol is a function that can perform non-blocking operations. It returns a generator that can be advanced with proceed() or awaited.
1# Define an async protocol2async protocol fetch_user(id):3 declare("Fetching user", id, "...")4 sleep(200) # Simulate network delay5 yield {6 "id": id,7 "name": "User " + text(id),8 "status": "active"9 }1011# Execute the async protocol12gen := fetch_user(42)13result := proceed(gen)14declare("Got user:", result)1516# Output:17# Fetching user 42 ...18# Got user: {...}1async protocol fetch_user(id):2 sleep(100)3 yield {"id": id, "name": "User " + text(id)}45async protocol fetch_posts(user_id):6 sleep(100)7 yield [8 {"title": "Post 1 by " + text(user_id)},9 {"title": "Post 2 by " + text(user_id)}10 ]1112async protocol get_user_with_posts(id):13 # Chain async calls14 user_gen := fetch_user(id)15 user := proceed(user_gen)16 17 posts_gen := fetch_posts(user["id"])18 posts := proceed(posts_gen)19 20 user["posts"] := posts21 yield user2223# Execute the chain24gen := get_user_with_posts(1)25data := proceed(gen)26declare("User with posts:", data)Promises represent values that will be available in the future. Use resolve() to create a promise and await to get its value.
1# Create a resolved promise2promise := resolve("completed")34# Await the promise5value := await promise6declare("Promise resolved with:", value)7# Output: Promise resolved with: completed89# Promises from async protocols10async protocol compute_heavy():11 sleep(500) # Expensive operation12 yield 42 * 421314# Start the operation (non-blocking)15computation := compute_heavy()1617# Do other work while computation runs18declare("Doing other work...")19sleep(100)20declare("Still working...")2122# Now get the result23result := proceed(computation)24declare("Computation result:", result)25# Output: 17641# Immediate resolution2immediate := resolve(100)3declare(await immediate) # 10045# Creating promise-like behavior6async protocol delayed_value(val, ms):7 sleep(ms)8 yield val910# Multiple concurrent operations11p1 := delayed_value("first", 300)12p2 := delayed_value("second", 200)13p3 := delayed_value("third", 100)1415# Results come back in completion order16declare(proceed(p3)) # third (100ms)17declare(proceed(p2)) # second (200ms)18declare(proceed(p1)) # first (300ms)The sleep(ms) function pauses execution for the specified milliseconds.
1declare("Starting...")23# Sleep for 1 second4sleep(1000)5declare("1 second passed")67# Sleep for half a second8sleep(500)9declare("1.5 seconds total")1011# Practical use: Rate limiting12protocol rate_limited_fetch(urls):13 cycle through urls as url:14 declare("Fetching:", url)15 sleep(100) # 100ms between requests16 yield "Data from " + url1718urls := ["api/1", "api/2", "api/3"]19cycle through rate_limited_fetch(urls) as data:20 declare("Received:", data)defer(ms, protocol, ...args) schedules a protocol to run after a delay without blocking.
1# Define a callback2protocol notify(message):3 declare("📢 Notification:", message)45# Schedule for later6defer(2000, notify, "Task completed!")7defer(1000, notify, "Still processing...")8defer(500, notify, "Started!")910declare("All tasks scheduled, continuing execution...")1112# Output order:13# All tasks scheduled, continuing execution...14# (after 500ms) 📢 Notification: Started!15# (after 1000ms) 📢 Notification: Still processing...16# (after 2000ms) 📢 Notification: Task completed!1# Simple debounce implementation2protocol handle_input(text):3 declare("Processing input:", text)45# Simulate rapid user input6defer(0, handle_input, "H")7defer(50, handle_input, "He")8defer(100, handle_input, "Hel")9defer(150, handle_input, "Hell")10defer(200, handle_input, "Hello") # Only process this1112# In practice, you'd cancel previous defers13# This is a simplified example1# Simulated API client2async protocol api_get(endpoint):3 declare("GET", endpoint)4 sleep(200) # Network latency5 6 foresee endpoint == "/users":7 yield [{"id": 1}, {"id": 2}]8 alternate endpoint == "/posts":9 yield [{"title": "Hello"}, {"title": "World"}]10 otherwise:11 yield {"error": "Not found"}1213# Usage14users_gen := api_get("/users")15posts_gen := api_get("/posts")1617users := proceed(users_gen)18posts := proceed(posts_gen)1920declare("Users:", users)21declare("Posts:", posts)1# Poll for status updates2async protocol poll_status(max_attempts):3 attempts := 04 cycle while attempts < max_attempts:5 attempts = attempts + 16 declare("Checking status (attempt", attempts, ")...")7 8 sleep(500)9 10 # Simulate random completion11 foresee random(1, 10) > 7:12 yield "completed"13 otherwise:14 yield "pending"1516gen := poll_status(5)17cycle:18 status := proceed(gen)19 declare("Status:", status)20 foresee status == "completed":21 declare("✓ Task finished!")22 terminate1# Execute with timeout2async protocol with_timeout(operation, timeout_ms):3 start := timestamp()4 gen := operation()5 6 result := proceed(gen)7 elapsed := (timestamp() - start) * 10008 9 foresee elapsed > timeout_ms:10 yield {"error": "Timeout exceeded"}11 otherwise:12 yield result1314async protocol slow_operation():15 sleep(2000)16 yield "Success"1718# This will timeout19gen := with_timeout(slow_operation, 1000)20result := proceed(gen)21declare(result) # {"error": "Timeout exceeded"}