-
Notifications
You must be signed in to change notification settings - Fork 0
/
flowcontrol.js
175 lines (156 loc) · 4.96 KB
/
flowcontrol.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* yet another...
* Flow Control !
* made by @dantaex on March 2014
*
*
* Usage
* var fc = require('flowcontrol');
* var mylist = [{...},...,{...}];
*
* var applyThisToEachElement = function(item,callback{
* //do your stuff
* ...
* //say i'm done
* callback(err,newdata);
* };
*
* fc.taskList( mylist, applyThisToEachElement, function(err,result){ console.log('Everything done, results in result var'); });
* //or
* fc.taskMap( mylist, applyThisToEachElement, function(err,result){ console.log('Everything done, results in result var'); });
* //or
* fc.taskChain( mylist, applyThisToEachElement, function(err,result){ console.log('Everything done, results in result var'); });
*
*
*/
/**
* @constructor
*/
function FlowControl(){}
/**
* Execute a function over an input list, no matter:
* Order of execution
* Order of output
* {f} Concurrecny limit
*
* @param {items} Array[] : input list
* @param {f} Function : this is the functon to be applied to each item in the input list,
* called as f(current_item,callWhenDone,items.length)
* @param {callback} Function : called as callback(errorlist) or callback(null,resultList)
* @param {opts.mergeArrayResults} boolean : There are certain moments when you have
* an array as a result of every f() execution, in this case you may end with
* something like this as final result:
* [ [{name:'a'},{name:'b'}], [{name:'c'}], [{name:'d'}], ... ]
* but you may want all of those results in a single array like this
* [ [{name:'a'},{name:'b'},{name:'c'},{name:'d'} ... ]
* so you can say mergeArrayResults
*/
FlowControl.prototype.taskList = function(items,f,callback,opts){
var notFinished = items.length,
errors = [],
results = [];
opts = opts || { mergeArrayResults : false, transport: {} };
function verifier(err,data){
if(err) errors.push(err);
else if(data){
if(data instanceof Array && opts.mergeArrayResults){
results = results.concat(data);
}
else
results.push(data);
}
if( --notFinished == 0 ){
if(errors.length > 0) callback(errors);
else callback(null,results);
}
}
if(items.length){
for (var i = items.length - 1; i >= 0; i--)
f(items[i],verifier,results.length,opts.transport);
}
else
callback(null,[]);
};
/**
* Execute a function over an input list, if you don't care about:
* Order of execution
* {f} Concurrecny limit
*
* But you need each output value to be stored in the same index of its corresponding input
*
* @param {items} Array[] : input list
* @param {f} Function : called f(current_item,callWhenDone,items.length)
* @param {callback} Function : called as callback(errorlist) or callback(null,resultList)
*/
FlowControl.prototype.taskMap = function(items,f,callback,opts){
var notFinished = items.length,
errors = [],
results = [];
opts = opts || { transport: {} };
function wrapper(i,item){
f(item,function(err,data){
if(err) errors.push(err);
else results[i] = data;
if( --notFinished == 0 ){
if(errors.length > 0) callback(errors);
else callback(null,results);
}
},results.length,opts.transport);
}
if(items.length){
for (var i = items.length - 1; i >= 0; i--)
wrapper(i,items[i]);
}
else
callback(null,[]);
};
/**
* Execute a function over an input list, if you care about:
* Order of execution
* {f} Concurrecny limit
* Each execution comes exactly after the last one finishes
*
* @param {items} Array[] : input list
* @param {f} Function : called f(current_item,callWhenDone,items.length)
* @param {callback} Function : called as callback(errorlist) or callback(null,resultList)
* @param {opts.acceptnull} boolean : Accept null results (default false)
* @param {opts.mergeArrayResults} boolean (default false) : There are certain moments when you have
* an array as a result of every f() execution, in this case you may end with
* something like this as final result:
* [ [{name:'a'},{name:'b'}], [{name:'c'}], [{name:'d'}], ... ]
* but you may want all of those results in a single array like this
* [ [{name:'a'},{name:'b'},{name:'c'},{name:'d'} ... ]
* so you can say mergeArrayResults
*/
// FlowControl.prototype.taskChain = function(items,f,callback,acceptnull,mergeArrayResults){
FlowControl.prototype.taskChain = function(items,f,callback,opts){
if(items == undefined){
return callback(null,[]);
}
opts = opts || { acceptnull : false, mergeArrayResults : false, transport : {} };
var nitems = items.slice(),
errors = [],
results = [];
function next(item){
if( item == undefined ){
if(errors.length > 0) callback( errors );
else callback( null, results );
} else {
f(item,function(err,data){
if(err) errors.push(err);
else{
if(opts.acceptnull || data != null)
if(data instanceof Array && opts.mergeArrayResults){
results = results.concat(data);
}
else
results.push(data);
}
next(nitems.shift());
},results.length,opts.transport);
}
}
next(nitems.shift());
};
if(typeof module != 'undefined')
module.exports = new FlowControl();