reduce.js 4.8 KB

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