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.
如果提供了毫秒數的參數,那麼表示在時限到來之前還沒有完成請求的話,將宣告並行工作失敗。預設情況下(沒有提供 untilliseconds),不管可選要求者怎麼樣,必要要求者完畢了可選要求者就完畢。如果提供了 untilliseconds 參數,就按照這個時限來對可選要求者限制。如果沒有提供必要要求者,那麼應該至少要傳入一個可選要求者,並且在規定時限完成的話,這個並行就成功了。
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)
回呼函數就是送人到要求者的那個回呼函數,可把要求者的值傳入這個回呼函數。回呼函數有兩個參數:成功或失敗。若 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.
當直接調用某個要求者時,你只需要傳入一個回呼函數,例如為了啟動多個步驟的工作需要調用 RQ.sequence 的結果。這工作的結果將是傳入到回呼函數中的第一個參數。
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)
取消函數是指會嘗試停止要求者執行的函數。取消函數可以使流程中不再需要的任務停止。例如 RQ.race 啟動了多個要求者,若其中一個要求者獲得了成功,那麼其餘的要求者則不再需要執行,會被全部取消執行。取消函數既不能代表復原(rollback),也不能代表後退(undo)。
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( . . . )
工廠函數就是生產要求者的函數。要求者的制定過程就是在工廠函數中利用那個參數完成的。RQ 提供的四個函數(RQ.sequence、RQ.parallel、RQ.race、RQ.fallback)皆是工廠函數。工廠函數能夠簡化程式開發。
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
有時候雖然能返回正確的結果,但如果太久的話那麼也相當於是失敗了。RQ 提供了一個可選的逾時值來限制 requestor 進行的請求時間。如果要求者花太多時間來工作了,那麼就應該要自動取消之。RQ.fallback 使得失敗有所恢複。
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
全稱要求者 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
請求化工廠 requestorize 可以返回一個要求者函數。這裡就是預先確定對回呼函數參數的處理(譯註:對要傳入到 callback 的參數進行處理,處理過程在 func 中,在執行工廠函數時確定)。
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;}
那麼我們接著可以將其轉變為要求者,就像 fullname_requestor:
We can turn it into a requestor that works just like the fullname_requestor:
var fullname_requestor = requestorize(make_fullname);
延時要求者 Delay Requestor
要求者 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); };}
真實要求者中,不是調用 setTimeout, 而是一個訊息將被發送到一個進程;而不是調用 clearTimeout,而是一個訊息將被發送到相同的進程以取消的工作。
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
構造工廠 construct 返回一個要求者,其參數是並行操作構成的數組另外,由於串列操作中需要對象結構的資料,所以亦安排了構造工作轉換這個對象。
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); };}
雖然 buildPage 要求者採用了數組的結構,但我們可以使用對象格式的結構,這樣的話,代碼閱讀起來會更能夠自我描述(譯註:更好讀)。通過構造工廠 construct 實現更簡單。
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([ getAd(adnet.klikHaus), getAd(adnet.inUFace), getAd(adnet.trackPipe) ]), 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]};
執行 respond 發起請求,然後傳入的回呼函數將會接受最終值,getId 會最先得到值。
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.