• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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