---
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]})
```
