# Douglas Crockford 大神写的 JavaScript 异步控制库：RQ（下）

## RQ 库 The RQ Library

RQ 仅仅是一个文件，rq.js。运行起来创建一个 RQ 变量，该变量包含四个函数。

RQ is delivered as a single file, rq.js. When run, it produces an RQ variable containing an object containing four functions.

#### RQ.sequence(requestors) RQ.sequence(requestors, milliseconds)

RQ.sequence 有一个请求者函数所构成的数组的参数，还有一个可选的参数，用于表示超时的。返回值是另一个总的请求者，它能够依次执行里面的每一个请求者，把上一个结果按照顺序地交给下一个。如果全体完成，那么最后一个请求者的结果将是全体的结果，以数组形式出现。如果数组中的一个请求者失败了，那么表示这个串行队列失败。数组并没有改变。

RQ.sequence takes an array of requestor functions and an optional time limit in milliseconds. It returns a requestor function that will start each of the requestors in order, giving the result of each to the next one. If all complete successfully, the result will be the result of the last requestor in the array. If any of the requestors in the array fail, then the sequence fails. The array is not modified.

If a milliseconds argument is provided, then the sequence will fail if it does not finish before the time limit.

#### RQ.parallel(requireds) RQ.parallel(requireds, milliseconds) RQ.parallel(requireds, optionals) RQ.parallel(requireds, milliseconds, optionals) RQ.parallel(requireds, optionals, untilliseconds) RQ.parallel(requireds, milliseconds, optionals, untilliseconds)

RQ.parallel 有一个必要请求者参数，为请求者函数所构成数组，及其用于表示超时的毫秒数参数（可选的）,另外还有一个可选请求者参数（也是数组），及其用于表示超时的毫秒数参数（可选的）。该函数会返回另外一个总的请求者，会立刻执行必要请求者和可选请求者。结果为所有请求者的结果。如果有两个数组传入，结果数组的长度是那个传入数组总数之和。第一个请求者的结果就是结果数组中的第一个元素。整个并行任务完成与否取决于那些必要请求者，必要请求者成功了则成功。可选请求者的成功与否不会影响整个请求任务。这可用于最佳的尝试，从而获得了可以达成的结果。数组并没有改变。

RQ.parallel takes an array of required requestor functions, and an optional time limit in milliseconds, and an optional array of optional requestors and an optional guaranteed time for the optional requestors. It returns a requestor function that will start all of the required and optional requestors at once. The result is an array containing all of the results from all of the requestors. If both arrays were provided, the length of the results array is the sum of the lengths of the two arrays. The result of the first requestor will be in the first position of the results array. The parallel will succeed only if all of the required requestors succeed. The array of optional requestors contains requests that can fail without causing the entire parallel operation to fail. It can be used as a best effort, obtaining the results that are attainable. The arrays are not modified.

If a milliseconds argument is provided, then the parallel will fail if all of the required requestors do not finish before the time limit. By default, the optionals have until all of the required requestors finish. The untilliseconds argument guarantees the optionals some amount of time. untilliseconds may not be larger than milliseconds. If the requireds array is empty, and if at least one optional requestor is successful within the allotted time, then the parallel succeeds.

RQ.parallel 并没有对 JavaScript 语言层面添加并行机制。它能让 JavaScript 程序充分利用语言层面原生的并行机制。程序自己并不是一脚包办所有事情，而且发出请求来让其他的进程或者机器来搞定事情，这些进程或者机器都是独立执行的。

RQ.parallel does not add parallelism to JavaScript. It allows JavaScript programs to effectively exploit the inherent parallelism of the universe. It is likely that many of the requestors will be communicating with other processes and other machines. Those other processes and machines will be executing independently.

#### RQ.race(requestors) RQ.race(requestors, milliseconds)

RQ.race 有一个请求者函数所构成的数组的参数，还有一个可选的参数，用于表示超时的。返回值是另一个总的请求者，它能够马上执行所有的请求者，但最终结果取得是最先成功的那次结果。如果数组中所有的请求者失败了，那么表示这个竞争失败。数组并没有改变。

If a milliseconds argument is provided, then the race will fail if it does not finish before the time limit.

#### RQ.fallback(requestors) RQ.fallback(requestors, milliseconds)

RQ.fallback 有一个请求者函数所构成的数组的参数，还有一个可选的参数，用于表示超时的。返回值是另一个总的请求者，虽然它也会如串行般依次执行，但只要有一个请求者执行成功了，那么剩余的请求者将不会执行。如果数组中所有的请求者失败了，那么表示这个串行队列失败。数组并没有改变。

RQ.fallback takes an array of requestor functions and an optional time limit in milliseconds. It returns a requestor function that will try each of the requestors in order until one is successful. If all of the requestors in the array fail, then the sequence fails. The array is not modified.

If a milliseconds argument is provided, then the fallback will fail if it does not finish before the time limit.

## 函数类型 Function Types

RQ 使用了四种类型的参数：requestors，callbacks，cancels和 factories。

RQ makes use of four types of functions: requestors, callbacks, cancels, and factories.

### 请求者 Requestor(callback, value)

A requestor is a function that represents some unit of work. It can be a simple function, or it can be a complex job, task, production step, or operation that will organize the work of many machines over a long period of time. A requestor function takes two arguments: A callback and an optional value. The callback will be used by the requestor to communicate its result. The optional value makes the result of a previous value in a sequence to the requestor.

A requestor may optionally return a cancel function that can be used to cancel the request.

### 回调函数 Callback(success, failure)

A callback is a function that is passed to a requestor so that the requestor can communicate its result. A callback can take two arguments, success and failure. If failure is falsy, then the requestor was successful.

You only have to provide a callback when calling a requestor directly, such as calling the result of RQ.sequence to start a multistep job. The result of the job will be the first argument passed to your callback function.

### 取消函数 Cancel(reason)

A cancel is a function that will attempt to stop the execution of a requestor. A cancel function makes it possible to stop the processing of a job that is no longer needed. For example, if several requestors are started by RQ.race and if one of the requestors produces an successful result, the results of the other requestors may be cancelled. Cancellation is intended to stop unnecessary work. Cancellation does not do rollbacks or undo.

A cancel function may optionally be returned by a requestor. If a requestor sends a message to another process requesting work, the cancel function should send a message to the same process indicating that the work is no longer needed.

### 工厂函数 Factory( . . . )

A factory is a function that makes requestor functions. A factory will usually take arguments that allow for the customization of a requestor. The four functions provided by RQ (RQ.sequence, RQ.parallel, RQ.race, RQ.fallback) are all factory functions. Factory functions can simplify application development.

## 超时 Timeouts

Sometimes a correct result that takes too long is indistinguishable from a failure. RQ provides optional timeout values that limit the amount of time that a requestor is allowed to take. If a requestor takes too long to do its work, it can be automatically cancelled. RQ.fallback makes such failures recoverable.

## 例子 Samples

### 特性请求者 Identity Requestor

identity_requestor 函数接收一个值并将其送到所传入的回调函数中。如果把 identity_requestor  放置于串行队列中，则仅仅是把前一个请求的结果交给下一个请求者。

The identity_requestor receives a value and delivers that value to its callback. If the identity requestor is placed in a sequence, it acts as a nop, sending the result of the previous requestor to the next requestor.

function identity_requestor(callback, value) {
return callback(value);
}

### 全称请求者 Fullname Requestor

The fullname_requestor receives an object and delivers a string made from properties of the object.

function fullname_requestor(callback, value) {
return callback(value.firstname + ' ' + value.lastname);
}

### 请求化工厂 Requestorize Factory

The requestorize factory can make a requestor from any function that takes a single argument.

function requestorize(func) {
return function requestor(callback, value) {
return callback(func(value));
};
}

We can use this to make processing steps in a sequence. For example, if we have a function that takes an object and returns a fullname:

function make_fullname(value) {
return value.firstname + ' ' + value.lastname;
}

We can turn it into a requestor that works just like the fullname_requestor:

var fullname_requestor = requestorize(make_fullname);


### 延时请求者 Delay Requestor

The delay_requestor inserts a delay into a sequence without blocking.

function delay_requestor(callback, value) {
var timeout_id = setTimeout(function () {
return callback(value);
}, 1000);
return function cancel(reason) {
return clearTimeout(timeout_id);
};
}

In a real requestor, instead of calling setTimeout, a message will be transmitted to a process, and instead of calling clearTimeout, a message will be transmitted to the same process to cancel the work.

### 延时工厂 Delay Factory

The delay factory simplifies the making of delay requestors.

function delay(milliseconds) {
return function requestor(callback, value) {
var timeout_id = setTimeout(function () {
return callback(value);
}, milliseconds);
return function cancel(reason) {
return clearTimeout(timeout_id);
};
};
}

### 构造工厂 Construct Factory

The construct factory makes a requestor that takes an array of results from a parallel operation and converts it into an object for use by subsequent operations in a sequence.

function construct(array) {
return function requestor(callback, value) {
var object = Object.create(null);
array.forEach(function (name, index) {
object[name] = value[index];
});
return callback(object);
};
}

We could write our buildPage requestor to take an array of values, but it makes more sense to give it an object so that its code will be self documenting. The construct factory makes that easy.

respond = RQ.fallback([
RQ.sequence([
getId,
getPreference,
RQ.parallel([
getNav,
RQ.race([
]),
RQ.fallback([
fetch("weather", localCache),
fetch("weather", localDB),
fetch("weather", remoteDB)
]),
getMessageOfTheDay
], [
getHoroscope,
getGossip
], 50),
construct(['nav', 'ads', 'weather', 'message', 'horoscope', 'gossip']),
buildPage
], 100),
planB
]};

To start things running, call respond, passing a callback function that will receive the final result, and the intial value that wll be given to getId.

05-17 42
03-31 2329