index.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. 'use strict';
  2. module.exports = function (PromiseArgument) {
  3. var Promise;
  4. function throat(size, fn) {
  5. var queue = new Queue();
  6. function run(fn, self, args) {
  7. if (size) {
  8. size--;
  9. var result = new Promise(function (resolve) {
  10. resolve(fn.apply(self, args));
  11. });
  12. result.then(release, release);
  13. return result;
  14. } else {
  15. return new Promise(function (resolve) {
  16. queue.push(new Delayed(resolve, fn, self, args));
  17. });
  18. }
  19. }
  20. function release() {
  21. size++;
  22. if (!queue.isEmpty()) {
  23. var next = queue.shift();
  24. next.resolve(run(next.fn, next.self, next.args));
  25. }
  26. }
  27. if (typeof size === 'function') {
  28. var temp = fn;
  29. fn = size;
  30. size = temp;
  31. }
  32. if (typeof size !== 'number') {
  33. throw new TypeError(
  34. 'Expected throat size to be a number but got ' + typeof size
  35. );
  36. }
  37. if (fn !== undefined && typeof fn !== 'function') {
  38. throw new TypeError(
  39. 'Expected throat fn to be a function but got ' + typeof fn
  40. );
  41. }
  42. if (typeof fn === 'function') {
  43. return function () {
  44. var args = [];
  45. for (var i = 0; i < arguments.length; i++) {
  46. args.push(arguments[i]);
  47. }
  48. return run(fn, this, args);
  49. };
  50. } else {
  51. return function (fn) {
  52. if (typeof fn !== 'function') {
  53. throw new TypeError(
  54. 'Expected throat fn to be a function but got ' + typeof fn
  55. );
  56. }
  57. var args = [];
  58. for (var i = 1; i < arguments.length; i++) {
  59. args.push(arguments[i]);
  60. }
  61. return run(fn, this, args);
  62. };
  63. }
  64. }
  65. if (arguments.length === 1 && typeof PromiseArgument === 'function') {
  66. Promise = PromiseArgument;
  67. return throat;
  68. } else {
  69. Promise = module.exports.Promise;
  70. if (typeof Promise !== 'function') {
  71. throw new Error(
  72. 'You must provide a Promise polyfill for this library to work in older environments'
  73. );
  74. }
  75. return throat(arguments[0], arguments[1]);
  76. }
  77. };
  78. /* istanbul ignore next */
  79. if (typeof Promise === 'function') {
  80. module.exports.Promise = Promise;
  81. }
  82. function Delayed(resolve, fn, self, args) {
  83. this.resolve = resolve;
  84. this.fn = fn;
  85. this.self = self || null;
  86. this.args = args;
  87. }
  88. function Queue() {
  89. this._s1 = [];
  90. this._s2 = [];
  91. }
  92. Queue.prototype.push = function (value) {
  93. this._s1.push(value);
  94. };
  95. Queue.prototype.shift = function () {
  96. var s2 = this._s2;
  97. if (s2.length === 0) {
  98. var s1 = this._s1;
  99. if (s1.length === 0) {
  100. return;
  101. }
  102. this._s1 = s2;
  103. s2 = this._s2 = s1.reverse();
  104. }
  105. return s2.pop();
  106. };
  107. Queue.prototype.isEmpty = function () {
  108. return !this._s1.length && !this._s2.length;
  109. };