reduce.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. "use strict";
  2. module.exports = function(Promise,
  3. PromiseArray,
  4. apiRejection,
  5. tryConvertToPromise,
  6. INTERNAL) {
  7. var async = require("./async.js");
  8. var util = require("./util.js");
  9. var tryCatch = util.tryCatch;
  10. var errorObj = util.errorObj;
  11. function ReductionPromiseArray(promises, fn, accum, _each) {
  12. this.constructor$(promises);
  13. this._promise._captureStackTrace();
  14. this._preservedValues = _each === INTERNAL ? [] : null;
  15. this._zerothIsAccum = (accum === undefined);
  16. this._gotAccum = false;
  17. this._reducingIndex = (this._zerothIsAccum ? 1 : 0);
  18. this._valuesPhase = undefined;
  19. var maybePromise = tryConvertToPromise(accum, this._promise);
  20. var rejected = false;
  21. var isPromise = maybePromise instanceof Promise;
  22. if (isPromise) {
  23. maybePromise = maybePromise._target();
  24. if (maybePromise._isPending()) {
  25. maybePromise._proxyPromiseArray(this, -1);
  26. } else if (maybePromise._isFulfilled()) {
  27. accum = maybePromise._value();
  28. this._gotAccum = true;
  29. } else {
  30. this._reject(maybePromise._reason());
  31. rejected = true;
  32. }
  33. }
  34. if (!(isPromise || this._zerothIsAccum)) this._gotAccum = true;
  35. this._callback = fn;
  36. this._accum = accum;
  37. if (!rejected) async.invoke(init, this, undefined);
  38. }
  39. function init() {
  40. this._init$(undefined, -5);
  41. }
  42. util.inherits(ReductionPromiseArray, PromiseArray);
  43. ReductionPromiseArray.prototype._init = function () {};
  44. ReductionPromiseArray.prototype._resolveEmptyArray = function () {
  45. if (this._gotAccum || this._zerothIsAccum) {
  46. this._resolve(this._preservedValues !== null
  47. ? [] : this._accum);
  48. }
  49. };
  50. ReductionPromiseArray.prototype._promiseFulfilled = function (value, index) {
  51. var values = this._values;
  52. values[index] = value;
  53. var length = this.length();
  54. var preservedValues = this._preservedValues;
  55. var isEach = preservedValues !== null;
  56. var gotAccum = this._gotAccum;
  57. var valuesPhase = this._valuesPhase;
  58. var valuesPhaseIndex;
  59. if (!valuesPhase) {
  60. valuesPhase = this._valuesPhase = new Array(length);
  61. for (valuesPhaseIndex=0; valuesPhaseIndex<length; ++valuesPhaseIndex) {
  62. valuesPhase[valuesPhaseIndex] = 0;
  63. }
  64. }
  65. valuesPhaseIndex = valuesPhase[index];
  66. if (index === 0 && this._zerothIsAccum) {
  67. this._accum = value;
  68. this._gotAccum = gotAccum = true;
  69. valuesPhase[index] = ((valuesPhaseIndex === 0)
  70. ? 1 : 2);
  71. } else if (index === -1) {
  72. this._accum = value;
  73. this._gotAccum = gotAccum = true;
  74. } else {
  75. if (valuesPhaseIndex === 0) {
  76. valuesPhase[index] = 1;
  77. } else {
  78. valuesPhase[index] = 2;
  79. this._accum = value;
  80. }
  81. }
  82. if (!gotAccum) return;
  83. var callback = this._callback;
  84. var receiver = this._promise._boundTo;
  85. var ret;
  86. for (var i = this._reducingIndex; i < length; ++i) {
  87. valuesPhaseIndex = valuesPhase[i];
  88. if (valuesPhaseIndex === 2) {
  89. this._reducingIndex = i + 1;
  90. continue;
  91. }
  92. if (valuesPhaseIndex !== 1) return;
  93. value = values[i];
  94. this._promise._pushContext();
  95. if (isEach) {
  96. preservedValues.push(value);
  97. ret = tryCatch(callback).call(receiver, value, i, length);
  98. }
  99. else {
  100. ret = tryCatch(callback)
  101. .call(receiver, this._accum, value, i, length);
  102. }
  103. this._promise._popContext();
  104. if (ret === errorObj) return this._reject(ret.e);
  105. var maybePromise = tryConvertToPromise(ret, this._promise);
  106. if (maybePromise instanceof Promise) {
  107. maybePromise = maybePromise._target();
  108. if (maybePromise._isPending()) {
  109. valuesPhase[i] = 4;
  110. return maybePromise._proxyPromiseArray(this, i);
  111. } else if (maybePromise._isFulfilled()) {
  112. ret = maybePromise._value();
  113. } else {
  114. return this._reject(maybePromise._reason());
  115. }
  116. }
  117. this._reducingIndex = i + 1;
  118. this._accum = ret;
  119. }
  120. this._resolve(isEach ? preservedValues : this._accum);
  121. };
  122. function reduce(promises, fn, initialValue, _each) {
  123. if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
  124. var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
  125. return array.promise();
  126. }
  127. Promise.prototype.reduce = function (fn, initialValue) {
  128. return reduce(this, fn, initialValue, null);
  129. };
  130. Promise.reduce = function (promises, fn, initialValue, _each) {
  131. return reduce(promises, fn, initialValue, _each);
  132. };
  133. };