@@ -6,10 +6,14 @@ function Deferred(fn, thisArg, args, resolve, reject) {
6
6
this . thisArg = thisArg ;
7
7
}
8
8
9
+ function defaultOnEmpty ( ) {
10
+ ++ this . concurrency ;
11
+ }
12
+
9
13
function next ( ) {
10
14
const d = this . pop ( ) ;
11
15
if ( d === undefined ) {
12
- ++ this . concurrency ;
16
+ this . onEmpty ( ) ;
13
17
} else {
14
18
try {
15
19
d . resolve ( d . fn . apply ( d . thisArg , d . args ) ) ;
@@ -20,10 +24,11 @@ function next() {
20
24
}
21
25
22
26
// based on implementation in https://github.com/ForbesLindesay/throat
23
- function Queue ( concurrency ) {
27
+ function Queue ( concurrency , onEmpty ) {
24
28
// not related to the queue implementation but used in this lib
25
29
this . concurrency = concurrency ;
26
30
this . next = next . bind ( this ) ;
31
+ this . onEmpty = onEmpty !== undefined ? onEmpty : defaultOnEmpty ;
27
32
28
33
this . _s1 = [ ] ; // stack to push to
29
34
this . _s2 = [ ] ; // stack to pop from
@@ -60,7 +65,7 @@ const { slice } = Array.prototype;
60
65
const makeLimiter = ( getQueue , termination = defaultTermination ) => {
61
66
return fn =>
62
67
function ( ) {
63
- const queue = getQueue ( this ) ;
68
+ const queue = getQueue ( this , arguments ) ;
64
69
const canRun = queue . concurrency > 0 ;
65
70
let argStart = 0 ;
66
71
const { length } = arguments ;
@@ -99,10 +104,24 @@ const limitFunction = (concurrency, opts) => {
99
104
const queue = new Queue ( concurrency ) ;
100
105
return makeLimiter ( ( ) => queue , opts ) ;
101
106
} ;
107
+ const limitFunctionWithKey = keyFunction => ( concurrency , opts ) => {
108
+ const queues = new Map ( ) ;
109
+ return makeLimiter ( ( thisArg , args ) => {
110
+ const key = keyFunction . apply ( thisArg , args ) ;
111
+ let queue = queues . get ( key ) ;
112
+ if ( queue === undefined ) {
113
+ queue = new Queue ( concurrency , ( ) => {
114
+ queues . delete ( key ) ;
115
+ } ) ;
116
+ queues . set ( key , queue ) ;
117
+ }
118
+ return queue ;
119
+ } ) ;
120
+ } ;
102
121
103
122
// create a method limiter where the concurrency is shared between all
104
123
// methods but locally to the instance
105
- export const limitMethod = ( concurrency , opts ) => {
124
+ const limitMethod = ( concurrency , opts ) => {
106
125
const queues = new WeakMap ( ) ;
107
126
return makeLimiter ( obj => {
108
127
let queue = queues . get ( obj ) ;
@@ -113,14 +132,30 @@ export const limitMethod = (concurrency, opts) => {
113
132
return queue ;
114
133
} , opts ) ;
115
134
} ;
135
+ const limitMethodWithKey = keyFunction => ( concurrency , opts ) => {
136
+ const queuesByInstance = new WeakMap ( ) ;
137
+ return makeLimiter ( ( thisArg , args ) => {
138
+ let queues = queuesByInstance . get ( thisArg ) ;
139
+ if ( queues === undefined ) {
140
+ queues = new Map ( ) ;
141
+ queuesByInstance . set ( thisArg , queues ) ;
142
+ }
143
+ let queue = queuesByInstance . get ( thisArg ) ;
144
+ if ( queue === undefined ) {
145
+ queue = new Queue ( concurrency ) ;
146
+ queuesByInstance . set ( thisArg , queue ) ;
147
+ }
148
+ return queue ;
149
+ } ) ;
150
+ } ;
116
151
117
- export default ( ...args ) => {
152
+ const makeDecorator = ( decorateFunction , decorateMethod ) => ( ...args ) => {
118
153
let method = false ;
119
154
let wrap ;
120
155
return ( target , key , descriptor ) => {
121
156
if ( key === undefined ) {
122
157
if ( wrap === undefined ) {
123
- wrap = limitFunction ( ...args ) ;
158
+ wrap = decorateFunction ( ...args ) ;
124
159
} else if ( method ) {
125
160
throw new Error (
126
161
"the same decorator cannot be used between function and method"
@@ -131,7 +166,7 @@ export default (...args) => {
131
166
132
167
if ( wrap === undefined ) {
133
168
method = true ;
134
- wrap = limitMethod ( ...args ) ;
169
+ wrap = decorateMethod ( ...args ) ;
135
170
} else if ( ! method ) {
136
171
throw new Error (
137
172
"the same decorator cannot be used between function and method"
@@ -150,3 +185,12 @@ export default (...args) => {
150
185
return descriptor ;
151
186
} ;
152
187
} ;
188
+
189
+ const decorator = makeDecorator ( limitFunction , limitMethod ) ;
190
+ decorator . withKey = keyFunction =>
191
+ makeDecorator (
192
+ limitFunctionWithKey ( keyFunction ) ,
193
+ limitMethodWithKey ( keyFunction )
194
+ ) ;
195
+
196
+ export { decorator as default } ;
0 commit comments