1'use strict' 2 3const defaultMaxRunning = 50 4 5const limit = module.exports = function (func, maxRunning) { 6 const state = {running: 0, queue: []} 7 if (!maxRunning) maxRunning = defaultMaxRunning 8 return function limited () { 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 === 0) 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) 34 } 35 } 36} 37 38module.exports.method = function (classOrObj, method, maxRunning) { 39 if (typeof classOrObj === 'function') { 40 const func = classOrObj.prototype[method] 41 classOrObj.prototype[method] = limit(func, maxRunning) 42 } else { 43 const func = classOrObj[method] 44 classOrObj[method] = limit(func, maxRunning) 45 } 46} 47 48module.exports.promise = function (func, maxRunning) { 49 const state = {running: 0, queue: []} 50 if (!maxRunning) maxRunning = defaultMaxRunning 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 === 0) 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) 73 }) 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 } 89} 90 91module.exports.promise.method = function (classOrObj, method, maxRunning) { 92 if (typeof classOrObj === 'function') { 93 const func = classOrObj.prototype[method] 94 classOrObj.prototype[method] = limit.promise(func, maxRunning) 95 } else { 96 const func = classOrObj[method] 97 classOrObj[method] = limit.promise(func, maxRunning) 98 } 99} 100