--- title: "Requestors" description: "Asynchronous work with requestors" weight: 25 type: "docs" --- Requestors are functions that encapsulate asynchronous work. They provide a structured way to compose callbacks, manage cancellation, and coordinate concurrent operations between actors. ## What is a Requestor A requestor is a function with this signature: ```javascript var my_requestor = function(callback, value) { // Do async work, then call callback with result // Return a cancel function } ``` - **callback** — called when the work completes: `callback(value, reason)` - On success: `callback(result)` or `callback(result, null)` - On failure: `callback(null, reason)` where reason explains the failure - **value** — input passed from the previous step (or the initial caller) - **return** — a cancel function, or null if cancellation is not supported The cancel function, when called, should abort the in-progress work. ## Writing a Requestor ```javascript var fetch_data = function(callback, url) { $contact(function(connection) { send(connection, {get: url}, function(response) { callback(response) }) }, {host: url, port: 80}) return function() { // clean up if needed } } ``` A requestor that always succeeds immediately: ```javascript var constant = function(callback, value) { callback(42) } ``` A requestor that always fails: ```javascript var broken = function(callback, value) { callback(null, "something went wrong") } ``` ## Composing Requestors ƿit provides four built-in functions for composing requestors into pipelines. ### sequence(requestor_array) Run requestors one after another. Each result becomes the input to the next. The final result is passed to the callback. ```javascript var pipeline = sequence([ fetch_user, validate_permissions, load_profile ]) pipeline(function(profile, reason) { if (reason) { print(reason) } else { print(profile.name) } }, user_id) ``` If any step fails, the remaining steps are skipped and the failure propagates. ### parallel(requestor_array, throttle, need) Start all requestors concurrently. Results are collected into an array matching the input order. ```javascript var both = parallel([ fetch_profile, fetch_settings ]) both(function(results, reason) { var profile = results[0] var settings = results[1] }, user_id) ``` - **throttle** — limit how many requestors run at once (null for no limit) - **need** — minimum number of successes required (default: all) ### race(requestor_array, throttle, need) Like `parallel`, but returns as soon as the needed number of results arrive. Unfinished requestors are cancelled. ```javascript var fastest = race([ fetch_from_cache, fetch_from_network, fetch_from_backup ]) fastest(function(results) { // results[0] is whichever responded first }, request) ``` Default need is 1. Useful for redundant operations where only one result matters. ### fallback(requestor_array) Try each requestor in order. If one fails, try the next. Return the first success. ```javascript var resilient = fallback([ fetch_from_primary, fetch_from_secondary, use_cached_value ]) resilient(function(data, reason) { if (reason) { print("all sources failed") } }, key) ``` ## Timeouts Wrap any requestor with `$time_limit` to add a timeout: ```javascript var timed = $time_limit(fetch_data, 5) // 5 second timeout timed(function(result, reason) { // reason will explain timeout if it fires }, url) ``` If the requestor does not complete within the time limit, it is cancelled and the callback receives a failure. ## Requestors and Actors Requestors are particularly useful with actor messaging. Since `send` is callback-based, it fits naturally: ```javascript var ask_worker = function(callback, task) { send(worker, task, function(reply) { callback(reply) }) } var pipeline = sequence([ ask_worker, process_result, store_result ]) pipeline(function(stored) { print("done") $stop() }, {type: "compute", data: [1, 2, 3]}) ```