map.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. var errorObj = util.errorObj;
  11. var async = Promise._async;
  12. function MappingPromiseArray(promises, fn, limit, _filter) {
  13. this.constructor$(promises);
  14. this._promise._captureStackTrace();
  15. var context = Promise._getContext();
  16. this._callback = util.contextBind(context, fn);
  17. this._preservedValues = _filter === INTERNAL
  18. ? new Array(this.length())
  19. : null;
  20. this._limit = limit;
  21. this._inFlight = 0;
  22. this._queue = [];
  23. async.invoke(this._asyncInit, this, undefined);
  24. if (util.isArray(promises)) {
  25. for (var i = 0; i < promises.length; ++i) {
  26. var maybePromise = promises[i];
  27. if (maybePromise instanceof Promise) {
  28. maybePromise.suppressUnhandledRejections();
  29. }
  30. }
  31. }
  32. }
  33. util.inherits(MappingPromiseArray, PromiseArray);
  34. MappingPromiseArray.prototype._asyncInit = function() {
  35. this._init$(undefined, -2);
  36. };
  37. MappingPromiseArray.prototype._init = function () {};
  38. MappingPromiseArray.prototype._promiseFulfilled = function (value, index) {
  39. var values = this._values;
  40. var length = this.length();
  41. var preservedValues = this._preservedValues;
  42. var limit = this._limit;
  43. if (index < 0) {
  44. index = (index * -1) - 1;
  45. values[index] = value;
  46. if (limit >= 1) {
  47. this._inFlight--;
  48. this._drainQueue();
  49. if (this._isResolved()) return true;
  50. }
  51. } else {
  52. if (limit >= 1 && this._inFlight >= limit) {
  53. values[index] = value;
  54. this._queue.push(index);
  55. return false;
  56. }
  57. if (preservedValues !== null) preservedValues[index] = value;
  58. var promise = this._promise;
  59. var callback = this._callback;
  60. var receiver = promise._boundValue();
  61. promise._pushContext();
  62. var ret = tryCatch(callback).call(receiver, value, index, length);
  63. var promiseCreated = promise._popContext();
  64. debug.checkForgottenReturns(
  65. ret,
  66. promiseCreated,
  67. preservedValues !== null ? "Promise.filter" : "Promise.map",
  68. promise
  69. );
  70. if (ret === errorObj) {
  71. this._reject(ret.e);
  72. return true;
  73. }
  74. var maybePromise = tryConvertToPromise(ret, this._promise);
  75. if (maybePromise instanceof Promise) {
  76. maybePromise = maybePromise._target();
  77. var bitField = maybePromise._bitField;
  78. ;
  79. if (((bitField & 50397184) === 0)) {
  80. if (limit >= 1) this._inFlight++;
  81. values[index] = maybePromise;
  82. maybePromise._proxy(this, (index + 1) * -1);
  83. return false;
  84. } else if (((bitField & 33554432) !== 0)) {
  85. ret = maybePromise._value();
  86. } else if (((bitField & 16777216) !== 0)) {
  87. this._reject(maybePromise._reason());
  88. return true;
  89. } else {
  90. this._cancel();
  91. return true;
  92. }
  93. }
  94. values[index] = ret;
  95. }
  96. var totalResolved = ++this._totalResolved;
  97. if (totalResolved >= length) {
  98. if (preservedValues !== null) {
  99. this._filter(values, preservedValues);
  100. } else {
  101. this._resolve(values);
  102. }
  103. return true;
  104. }
  105. return false;
  106. };
  107. MappingPromiseArray.prototype._drainQueue = function () {
  108. var queue = this._queue;
  109. var limit = this._limit;
  110. var values = this._values;
  111. while (queue.length > 0 && this._inFlight < limit) {
  112. if (this._isResolved()) return;
  113. var index = queue.pop();
  114. this._promiseFulfilled(values[index], index);
  115. }
  116. };
  117. MappingPromiseArray.prototype._filter = function (booleans, values) {
  118. var len = values.length;
  119. var ret = new Array(len);
  120. var j = 0;
  121. for (var i = 0; i < len; ++i) {
  122. if (booleans[i]) ret[j++] = values[i];
  123. }
  124. ret.length = j;
  125. this._resolve(ret);
  126. };
  127. MappingPromiseArray.prototype.preservedValues = function () {
  128. return this._preservedValues;
  129. };
  130. function map(promises, fn, options, _filter) {
  131. if (typeof fn !== "function") {
  132. return apiRejection("expecting a function but got " + util.classString(fn));
  133. }
  134. var limit = 0;
  135. if (options !== undefined) {
  136. if (typeof options === "object" && options !== null) {
  137. if (typeof options.concurrency !== "number") {
  138. return Promise.reject(
  139. new TypeError("'concurrency' must be a number but it is " +
  140. util.classString(options.concurrency)));
  141. }
  142. limit = options.concurrency;
  143. } else {
  144. return Promise.reject(new TypeError(
  145. "options argument must be an object but it is " +
  146. util.classString(options)));
  147. }
  148. }
  149. limit = typeof limit === "number" &&
  150. isFinite(limit) && limit >= 1 ? limit : 0;
  151. return new MappingPromiseArray(promises, fn, limit, _filter).promise();
  152. }
  153. Promise.prototype.map = function (fn, options) {
  154. return map(this, fn, options, null);
  155. };
  156. Promise.map = function (promises, fn, options, _filter) {
  157. return map(promises, fn, options, _filter);
  158. };
  159. };