|
1 | 1 | "use strict" |
2 | 2 |
|
3 | | -var defaultMaxRunning = 50 |
| 3 | +const defaultMaxRunning = 50 |
4 | 4 |
|
5 | | -var limit = module.exports = function (func, maxRunning) { |
6 | | - var running = 0 |
7 | | - var queue = [] |
| 5 | +const limit = module.exports = function (func, maxRunning) { |
| 6 | + const state = {running: 0, queue: []} |
8 | 7 | if (!maxRunning) maxRunning = defaultMaxRunning |
9 | 8 | return function limited () { |
10 | | - var self = this |
11 | | - var args = Array.prototype.slice.call(arguments) |
12 | | - if (running >= maxRunning) { |
13 | | - queue.push({self: this, args: args}) |
14 | | - return |
| 9 | + const args = Array.prototype.slice.call(arguments) |
| 10 | + if (state.running >= maxRunning) { |
| 11 | + state.queue.push({obj: this, args}) |
| 12 | + } else { |
| 13 | + callFunc(this, args) |
| 14 | + } |
| 15 | + } |
| 16 | + function callNext () { |
| 17 | + if (!state.queue.length) return |
| 18 | + const next = state.queue.shift() |
| 19 | + callFunc(next.obj, next.args) |
| 20 | + } |
| 21 | + function callFunc (obj, args) { |
| 22 | + const cb = typeof args[args.length-1] === 'function' && args.pop() |
| 23 | + try { |
| 24 | + ++state.running |
| 25 | + func.apply(obj, args.concat(function () { |
| 26 | + --state.running |
| 27 | + process.nextTick(callNext) |
| 28 | + if (cb) process.nextTick(() => cb.apply(obj, arguments)) |
| 29 | + })) |
| 30 | + } catch (err) { |
| 31 | + --state.running |
| 32 | + if (cb) process.nextTick(() => cb.call(obj, err)) |
| 33 | + process.nextTick(callNext) |
15 | 34 | } |
16 | | - var cb = typeof args[args.length-1] === 'function' && args.pop() |
17 | | - ++ running |
18 | | - args.push(function () { |
19 | | - var cbargs = arguments |
20 | | - -- running |
21 | | - cb && process.nextTick(function () { |
22 | | - cb.apply(self, cbargs) |
23 | | - }) |
24 | | - if (queue.length) { |
25 | | - var next = queue.shift() |
26 | | - limited.apply(next.self, next.args) |
27 | | - } |
28 | | - }) |
29 | | - func.apply(self, args) |
30 | 35 | } |
31 | 36 | } |
32 | 37 |
|
33 | 38 | module.exports.method = function (classOrObj, method, maxRunning) { |
34 | 39 | if (typeof classOrObj === 'function') { |
35 | | - var func = classOrObj.prototype[method] |
| 40 | + const func = classOrObj.prototype[method] |
36 | 41 | classOrObj.prototype[method] = limit(func, maxRunning) |
37 | 42 | } else { |
38 | | - var func = classOrObj[method] |
| 43 | + const func = classOrObj[method] |
39 | 44 | classOrObj[method] = limit(func, maxRunning) |
40 | 45 | } |
41 | 46 | } |
42 | 47 |
|
43 | 48 | module.exports.promise = function (func, maxRunning) { |
44 | | - var running = 0 |
45 | | - var queue = [] |
| 49 | + const state = {running: 0, queue: []} |
46 | 50 | if (!maxRunning) maxRunning = defaultMaxRunning |
47 | | - return function () { |
48 | | - var self = this |
49 | | - var args = Array.prototype.slice.call(arguments) |
50 | | - return new Promise(function (resolve) { |
51 | | - if (running >= maxRunning) { |
52 | | - queue.push({self: self, args: args, resolve: resolve}) |
53 | | - return |
54 | | - } else { |
55 | | - runNext(self, args, resolve) |
56 | | - } |
57 | | - function runNext (self, args, resolve) { |
58 | | - ++ running |
59 | | - resolve( |
60 | | - func.apply(self, args) |
61 | | - .then(function (value) { |
62 | | - finish() |
63 | | - return value |
64 | | - }, function (err) { |
65 | | - finish(err) |
66 | | - throw err |
67 | | - })) |
68 | | - } |
69 | | - |
70 | | - function finish () { |
71 | | - -- running |
72 | | - if (queue.length) { |
73 | | - var next = queue.shift() |
74 | | - process.nextTick(runNext, next.self, next.args, next.resolve) |
75 | | - } |
76 | | - } |
| 51 | + return function limited () { |
| 52 | + const args = Array.prototype.slice.call(arguments) |
| 53 | + if (state.running >= maxRunning) { |
| 54 | + return new Promise(resolve => { |
| 55 | + state.queue.push({resolve, obj: this, args}) |
| 56 | + }) |
| 57 | + } else { |
| 58 | + return callFunc(this, args) |
| 59 | + } |
| 60 | + } |
| 61 | + function callNext () { |
| 62 | + if (!state.queue.length) return |
| 63 | + const next = state.queue.shift() |
| 64 | + next.resolve(callFunc(next.obj, next.args)) |
| 65 | + } |
| 66 | + function callFunc (obj, args) { |
| 67 | + return callFinally(() => { |
| 68 | + ++state.running |
| 69 | + return func.apply(obj, args) |
| 70 | + }, () => { |
| 71 | + --state.running |
| 72 | + process.nextTick(callNext) |
77 | 73 | }) |
78 | 74 | } |
| 75 | + function callFinally (action, fin) { |
| 76 | + try { |
| 77 | + return Promise.resolve(action()).then(value => { |
| 78 | + fin() |
| 79 | + return value |
| 80 | + }, err => { |
| 81 | + fin() |
| 82 | + return Promise.reject(err) |
| 83 | + }) |
| 84 | + } catch (err) { |
| 85 | + fin() |
| 86 | + return Promise.reject(err) |
| 87 | + } |
| 88 | + } |
79 | 89 | } |
80 | 90 |
|
81 | 91 | module.exports.promise.method = function (classOrObj, method, maxRunning) { |
82 | 92 | if (typeof classOrObj === 'function') { |
83 | | - var func = classOrObj.prototype[method] |
| 93 | + const func = classOrObj.prototype[method] |
84 | 94 | classOrObj.prototype[method] = limit.promise(func, maxRunning) |
85 | 95 | } else { |
86 | | - var func = classOrObj[method] |
| 96 | + const func = classOrObj[method] |
87 | 97 | classOrObj[method] = limit.promise(func, maxRunning) |
88 | 98 | } |
89 | 99 | } |
0 commit comments