|
| 1 | +;(function(global) { |
| 2 | + 'use strict' |
| 3 | + |
| 4 | + var nextTick = function (fn) { setTimeout(fn, 0) } |
| 5 | + if (typeof process !== 'undefined' && process && typeof process.nextTick === 'function') { |
| 6 | + // node.js and the like |
| 7 | + nextTick = process.nextTick |
| 8 | + } |
| 9 | + |
| 10 | + function checkNumber (n) { |
| 11 | + if (typeof n !== 'number' || isNaN(n) || !isFinite(n)) { |
| 12 | + throw new TypeError('expected number, got ' + n) |
| 13 | + } |
| 14 | + if (n % 1 !== 0 || n < 1) { |
| 15 | + throw new RangeError('expected positive integer number, got ' + n) |
| 16 | + } |
| 17 | + } |
| 18 | + |
| 19 | + function checkFunction (f) { |
| 20 | + if (typeof f !== 'function') throw new TypeError('expected function, got ' + f) |
| 21 | + } |
| 22 | + |
| 23 | + function Semaphore (capacity) { |
| 24 | + if (!(this instanceof Semaphore)) { |
| 25 | + return new Semaphore(capacity) |
| 26 | + } |
| 27 | + |
| 28 | + this._capacity = capacity || 1 |
| 29 | + checkNumber(this._capacity) |
| 30 | + |
| 31 | + this._current = 0 |
| 32 | + this._queue = [] |
| 33 | + this._leave = this.leave.bind(this) |
| 34 | + } |
| 35 | + |
| 36 | + Semaphore.prototype.take = function () { |
| 37 | + var item = {} |
| 38 | + if (typeof arguments[0] === 'function') { |
| 39 | + item.task = arguments[0] |
| 40 | + item.n = arguments[1] || 1 |
| 41 | + } else { |
| 42 | + item.task = arguments[1] |
| 43 | + item.n = arguments[0] || 1 |
| 44 | + } |
| 45 | + |
| 46 | + checkFunction(item.task) |
| 47 | + checkNumber(item.n) |
| 48 | + if (item.n > this._capacity) throw new RangeError('expected number in [1, ' + this._capacity + '], got ' + item.n) |
| 49 | + |
| 50 | + if (this._current + item.n > this._capacity) { |
| 51 | + this._queue.push(item) |
| 52 | + } else { |
| 53 | + this._current += item.n |
| 54 | + item.task(this._leave) |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + Semaphore.prototype.leave = function (n) { |
| 59 | + n = n || 1 |
| 60 | + checkNumber(n) |
| 61 | + |
| 62 | + this._current = Math.max(this._current - n, 0) |
| 63 | + |
| 64 | + if (this._queue.length === 0) return |
| 65 | + if (this._current + this._queue[0].n > this._capacity) return |
| 66 | + |
| 67 | + var item = this._queue.shift() |
| 68 | + this._current += item.n |
| 69 | + |
| 70 | + var leave = this._leave |
| 71 | + nextTick(function () { item.task(leave) }) |
| 72 | + } |
| 73 | + |
| 74 | + if (typeof exports === 'object') { |
| 75 | + // node export |
| 76 | + module.exports = Semaphore |
| 77 | + } else if (typeof define === 'function' && define.amd) { |
| 78 | + // amd export |
| 79 | + define(function () { return Semaphore }) |
| 80 | + } else { |
| 81 | + // browser global |
| 82 | + global.semaphore = global.Semaphore = Semaphore |
| 83 | + } |
| 84 | +}(this)) |
0 commit comments