177 lines
4.0 KiB
Markdown
177 lines
4.0 KiB
Markdown
---
|
|
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]})
|
|
```
|