Zrobiłem całkiem sporo tego w ostatnim projekcie. Napisałem kod, aby pomóc w zarządzaniu nim. Oto kod. Przekazujesz funkcję bundleAsync obiektowi z parametrem "wywołania" i parametrem "bundleCallback". Parametr call jest tablicą obiektów reprezentujących funkcję, którą chcesz wywołać. W parametrze fn zapisywane jest odniesienie do faktycznego parametru. W parametrze "args" przechowujesz argumenty. Ostatnim argumentem każdej z funkcji, które przekazujesz, musi być wywołanie zwrotne, które musi zostać wywołane.
jestem nieszczęśliwy w dokumentowanie mojego kodu i czyni go użytecznym dla innych, ale to jest naprawdę bardzo przydatne dla mnie. Podejrzewam, że ktoś napisał coś podobnego, być może odpowiednio udokumentowanego. Jeśli nie możesz tego znaleźć i potrzebujesz pomocy w ustaleniu tego, daj mi znać.
/**
This is a way to submit multiple async calls, and be notified when they've all finished
<pre>
NameSpace.bundledAsync({
calls:[
{
fn: service.getGroups,
args: [
function(listsArg){
listsSummary = listsArg;
}
],
calls: function(){return UNAB.Util.makeArray(listsSummary, function(list){
return {
fn: service.getGroup,
args: [list.id, function(resp){listsDetail.push(resp)}]
}
})}
}
],
bundleCallback: function(){
callback(listsDetail)
}
});
</pre>
@class bundledAsync
@static
*/
NameSpace.bundledAsync = function(options){
var callbacksLeft = 0;
var calls = $.grep(options.calls, function(call){return call});
if(options.hasOwnProperty("bundleCallback") && typeof options.bundleCallback != "function"){
throw new Error("bundleCallback, passed to bundledAsync, must be a function.");
}
if(options.chain){ // if this is true, sibling calls will run in succession, not in parallel
calls.reverse();
var newCalls = [calls.pop()];
var lastCall = newCalls[0];
while(calls.length > 0){
if(lastCall.calls){
throw new Error("You can't nest calls if you're in chain mode");
}
lastCall.calls = [calls.pop()];
lastCall = lastCall.calls[0];
}
calls = newCalls;
}
var decrimentCallbacksLeft = function(){
if(options.name){
// log.debug("Starting decrimentCallbacksLeft for: " + options.name + ". Decrimenting callbacksLeft to: " + (callbacksLeft - 1));
}
if(--callbacksLeft == 0 && options.bundleCallback){
// log.debug("No callbacks left. Calling bundleCallback for name: " + options.name);
options.bundleCallback();
}
}
var doCalls = function(callsToDo){
if(typeof callsToDo == "function"){
callsToDo = callsToDo();
}else{
callsToDo = $.extend(true, [], callsToDo);// in case we want to reuse the calls
}
// right away, return if the calls are empty
// check to make sure callbacksLeft == 0, because
// we may be dealing with nested calls
if(callsToDo.length ==0 && callbacksLeft == 0){
// log.debug("callsToDo is empty, so call the callback right away.");
options.bundleCallback();
return null;
}
callbacksLeft += callsToDo.length;
$.each(callsToDo, function(index, call){
var numFns = 0;
// // Look through the args searching for functions.
// // When one is found, wrap it with our own function.
// // This assumes that each function has exactly one
// // callback, and that each callback is called exactly once
// args can be a function which will return the args,
// that way, you don't have to determine the args for the function until the moment it's called
call.args = call.jitArgs? call.args():call.args;
$.each(call.args, function(index, arg){
if(typeof arg === "function"){
numFns++;
// Here's where we wrap the original function's callback
call.args[index] = function(){
// when we get to this point, we know that the original function has totally completed,
// and we can call any functions chained to this one, or finish the whole process
arg.apply(null, arguments); // call the original callback
if(call.calls){
// maybe we don't want to create the child calls until after
// the parent has returned. In that case, pass a function instead of an array
if(typeof call.calls === "function"){
call.calls = call.calls();
}
// if this call has any call of its own, send those out now
doCalls(call.calls);
}
decrimentCallbacksLeft();
}
}
});
if(numFns!=1){
throw new Error("Each function passed to bundledAsync must have one and only one arg which is a function");
}
// if(call.fn.length != call.args.length){
// log.warn("The current function is being called with a different number of arguments that that with which it was declared. Should be: "+call.fn.length+", was: "+call.args.length+" \n" + call.fn.toString());
// }
call.fn.apply(null, call.args);
});
}
doCalls(calls);
}
... Czy mógłbyś wyjaśnić bardziej szczegółowo, co chcesz zrobić? – deceze
Chciałbym napisać tę kontrolę przepływu, ale nie wiem jak. Przeglądałem dużo kodu, ale go nie dostałem. –
Czy funkcje są potencjalnie asynchroniczne? – slebetman