4.0 KiB
title, description, weight, type
| title | description | weight | type |
|---|---|---|---|
| Requestors | Asynchronous work with requestors | 25 | 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:
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)orcallback(result, null) - On failure:
callback(null, reason)where reason explains the failure
- On success:
- 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
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:
var constant = function(callback, value) {
callback(42)
}
A requestor that always fails:
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.
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.
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.
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.
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:
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:
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]})