update
This commit is contained in:
176
docs/requestors.md
Normal file
176
docs/requestors.md
Normal file
@@ -0,0 +1,176 @@
|
||||
---
|
||||
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
|
||||
function my_requestor(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
|
||||
function fetch_data(callback, url) {
|
||||
$contact(function(connection) {
|
||||
$send(connection, {get: url}, function(response) {
|
||||
callback(response)
|
||||
})
|
||||
}, {host: url, port: 80})
|
||||
return function cancel() {
|
||||
// clean up if needed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A requestor that always succeeds immediately:
|
||||
|
||||
```javascript
|
||||
function constant(callback, value) {
|
||||
callback(42)
|
||||
}
|
||||
```
|
||||
|
||||
A requestor that always fails:
|
||||
|
||||
```javascript
|
||||
function broken(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) {
|
||||
log.error(reason)
|
||||
} else {
|
||||
log.console(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) {
|
||||
log.error("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
|
||||
function ask_worker(callback, task) {
|
||||
$send(worker, task, function(reply) {
|
||||
callback(reply)
|
||||
})
|
||||
}
|
||||
|
||||
var pipeline = sequence([
|
||||
ask_worker,
|
||||
process_result,
|
||||
store_result
|
||||
])
|
||||
|
||||
pipeline(function(stored) {
|
||||
log.console("done")
|
||||
$stop()
|
||||
}, {type: "compute", data: [1, 2, 3]})
|
||||
```
|
||||
Reference in New Issue
Block a user