reduce.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. "use strict";
  2. module.exports = function(Promise,
  3. PromiseArray,
  4. apiRejection,
  5. tryConvertToPromise,
  6. INTERNAL,
  7. debug) {
  8. var getDomain = Promise._getDomain;
  9. var util = require("./util");
  10. var tryCatch = util.tryCatch;
  11. function ReductionPromiseArray(promises, fn, initialValue, _each) {
  12. this.constructor$(promises);
  13. var domain = getDomain();
  14. this._fn = domain === null ? fn : util.domainBind(domain, fn);
  15. if (initialValue !== undefined) {
  16. initialValue = Promise.resolve(initialValue);
  17. initialValue._attachCancellationCallback(this);
  18. }
  19. this._initialValue = initialValue;
  20. this._currentCancellable = null;
  21. if(_each === INTERNAL) {
  22. this._eachValues = Array(this._length);
  23. } else if (_each === 0) {
  24. this._eachValues = null;
  25. } else {
  26. this._eachValues = undefined;
  27. }
  28. this._promise._captureStackTrace();
  29. this._init$(undefined, -5);
  30. }
  31. util.inherits(ReductionPromiseArray, PromiseArray);
  32. ReductionPromiseArray.prototype._gotAccum = function(accum) {
  33. if (this._eachValues !== undefined &&
  34. this._eachValues !== null &&
  35. accum !== INTERNAL) {
  36. this._eachValues.push(accum);
  37. }
  38. };
  39. ReductionPromiseArray.prototype._eachComplete = function(value) {
  40. if (this._eachValues !== null) {
  41. this._eachValues.push(value);
  42. }
  43. return this._eachValues;
  44. };
  45. ReductionPromiseArray.prototype._init = function() {};
  46. ReductionPromiseArray.prototype._resolveEmptyArray = function() {
  47. this._resolve(this._eachValues !== undefined ? this._eachValues
  48. : this._initialValue);
  49. };
  50. ReductionPromiseArray.prototype.shouldCopyValues = function () {
  51. return false;
  52. };
  53. ReductionPromiseArray.prototype._resolve = function(value) {
  54. this._promise._resolveCallback(value);
  55. this._values = null;
  56. };
  57. ReductionPromiseArray.prototype._resultCancelled = function(sender) {
  58. if (sender === this._initialValue) return this._cancel();
  59. if (this._isResolved()) return;
  60. this._resultCancelled$();
  61. if (this._currentCancellable instanceof Promise) {
  62. this._currentCancellable.cancel();
  63. }
  64. if (this._initialValue instanceof Promise) {
  65. this._initialValue.cancel();
  66. }
  67. };
  68. ReductionPromiseArray.prototype._iterate = function (values) {
  69. this._values = values;
  70. var value;
  71. var i;
  72. var length = values.length;
  73. if (this._initialValue !== undefined) {
  74. value = this._initialValue;
  75. i = 0;
  76. } else {
  77. value = Promise.resolve(values[0]);
  78. i = 1;
  79. }
  80. this._currentCancellable = value;
  81. if (!value.isRejected()) {
  82. for (; i < length; ++i) {
  83. var ctx = {
  84. accum: null,
  85. value: values[i],
  86. index: i,
  87. length: length,
  88. array: this
  89. };
  90. value = value._then(gotAccum, undefined, undefined, ctx, undefined);
  91. }
  92. }
  93. if (this._eachValues !== undefined) {
  94. value = value
  95. ._then(this._eachComplete, undefined, undefined, this, undefined);
  96. }
  97. value._then(completed, completed, undefined, value, this);
  98. };
  99. Promise.prototype.reduce = function (fn, initialValue) {
  100. return reduce(this, fn, initialValue, null);
  101. };
  102. Promise.reduce = function (promises, fn, initialValue, _each) {
  103. return reduce(promises, fn, initialValue, _each);
  104. };
  105. function completed(valueOrReason, array) {
  106. if (this.isFulfilled()) {
  107. array._resolve(valueOrReason);
  108. } else {
  109. array._reject(valueOrReason);
  110. }
  111. }
  112. function reduce(promises, fn, initialValue, _each) {
  113. if (typeof fn !== "function") {
  114. return apiRejection("expecting a function but got " + util.classString(fn));
  115. }
  116. var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
  117. return array.promise();
  118. }
  119. function gotAccum(accum) {
  120. this.accum = accum;
  121. this.array._gotAccum(accum);
  122. var value = tryConvertToPromise(this.value, this.array._promise);
  123. if (value instanceof Promise) {
  124. this.array._currentCancellable = value;
  125. return value._then(gotValue, undefined, undefined, this, undefined);
  126. } else {
  127. return gotValue.call(this, value);
  128. }
  129. }
  130. function gotValue(value) {
  131. var array = this.array;
  132. var promise = array._promise;
  133. var fn = tryCatch(array._fn);
  134. promise._pushContext();
  135. var ret;
  136. if (array._eachValues !== undefined) {
  137. ret = fn.call(promise._boundValue(), value, this.index, this.length);
  138. } else {
  139. ret = fn.call(promise._boundValue(),
  140. this.accum, value, this.index, this.length);
  141. }
  142. if (ret instanceof Promise) {
  143. array._currentCancellable = ret;
  144. }
  145. var promiseCreated = promise._popContext();
  146. debug.checkForgottenReturns(
  147. ret,
  148. promiseCreated,
  149. array._eachValues !== undefined ? "Promise.each" : "Promise.reduce",
  150. promise
  151. );
  152. return ret;
  153. }
  154. };