reduce.js 5.2 KB

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