using.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. "use strict";
  2. module.exports = function (Promise, apiRejection, tryConvertToPromise,
  3. createContext, INTERNAL, debug) {
  4. var util = require("./util");
  5. var TypeError = require("./errors").TypeError;
  6. var inherits = require("./util").inherits;
  7. var errorObj = util.errorObj;
  8. var tryCatch = util.tryCatch;
  9. function thrower(e) {
  10. setTimeout(function(){throw e;}, 0);
  11. }
  12. function castPreservingDisposable(thenable) {
  13. var maybePromise = tryConvertToPromise(thenable);
  14. if (maybePromise !== thenable &&
  15. typeof thenable._isDisposable === "function" &&
  16. typeof thenable._getDisposer === "function" &&
  17. thenable._isDisposable()) {
  18. maybePromise._setDisposable(thenable._getDisposer());
  19. }
  20. return maybePromise;
  21. }
  22. function dispose(resources, inspection) {
  23. var i = 0;
  24. var len = resources.length;
  25. var ret = new Promise(INTERNAL);
  26. function iterator() {
  27. if (i >= len) return ret._fulfill();
  28. var maybePromise = castPreservingDisposable(resources[i++]);
  29. if (maybePromise instanceof Promise &&
  30. maybePromise._isDisposable()) {
  31. try {
  32. maybePromise = tryConvertToPromise(
  33. maybePromise._getDisposer().tryDispose(inspection),
  34. resources.promise);
  35. } catch (e) {
  36. return thrower(e);
  37. }
  38. if (maybePromise instanceof Promise) {
  39. return maybePromise._then(iterator, thrower,
  40. null, null, null);
  41. }
  42. }
  43. iterator();
  44. }
  45. iterator();
  46. return ret;
  47. }
  48. function Disposer(data, promise, context) {
  49. this._data = data;
  50. this._promise = promise;
  51. this._context = context;
  52. }
  53. Disposer.prototype.data = function () {
  54. return this._data;
  55. };
  56. Disposer.prototype.promise = function () {
  57. return this._promise;
  58. };
  59. Disposer.prototype.resource = function () {
  60. if (this.promise().isFulfilled()) {
  61. return this.promise().value();
  62. }
  63. return null;
  64. };
  65. Disposer.prototype.tryDispose = function(inspection) {
  66. var resource = this.resource();
  67. var context = this._context;
  68. if (context !== undefined) context._pushContext();
  69. var ret = resource !== null
  70. ? this.doDispose(resource, inspection) : null;
  71. if (context !== undefined) context._popContext();
  72. this._promise._unsetDisposable();
  73. this._data = null;
  74. return ret;
  75. };
  76. Disposer.isDisposer = function (d) {
  77. return (d != null &&
  78. typeof d.resource === "function" &&
  79. typeof d.tryDispose === "function");
  80. };
  81. function FunctionDisposer(fn, promise, context) {
  82. this.constructor$(fn, promise, context);
  83. }
  84. inherits(FunctionDisposer, Disposer);
  85. FunctionDisposer.prototype.doDispose = function (resource, inspection) {
  86. var fn = this.data();
  87. return fn.call(resource, resource, inspection);
  88. };
  89. function maybeUnwrapDisposer(value) {
  90. if (Disposer.isDisposer(value)) {
  91. this.resources[this.index]._setDisposable(value);
  92. return value.promise();
  93. }
  94. return value;
  95. }
  96. function ResourceList(length) {
  97. this.length = length;
  98. this.promise = null;
  99. this[length-1] = null;
  100. }
  101. ResourceList.prototype._resultCancelled = function() {
  102. var len = this.length;
  103. for (var i = 0; i < len; ++i) {
  104. var item = this[i];
  105. if (item instanceof Promise) {
  106. item.cancel();
  107. }
  108. }
  109. };
  110. Promise.using = function () {
  111. var len = arguments.length;
  112. if (len < 2) return apiRejection(
  113. "you must pass at least 2 arguments to Promise.using");
  114. var fn = arguments[len - 1];
  115. if (typeof fn !== "function") {
  116. return apiRejection("expecting a function but got " + util.classString(fn));
  117. }
  118. var input;
  119. var spreadArgs = true;
  120. if (len === 2 && Array.isArray(arguments[0])) {
  121. input = arguments[0];
  122. len = input.length;
  123. spreadArgs = false;
  124. } else {
  125. input = arguments;
  126. len--;
  127. }
  128. var resources = new ResourceList(len);
  129. for (var i = 0; i < len; ++i) {
  130. var resource = input[i];
  131. if (Disposer.isDisposer(resource)) {
  132. var disposer = resource;
  133. resource = resource.promise();
  134. resource._setDisposable(disposer);
  135. } else {
  136. var maybePromise = tryConvertToPromise(resource);
  137. if (maybePromise instanceof Promise) {
  138. resource =
  139. maybePromise._then(maybeUnwrapDisposer, null, null, {
  140. resources: resources,
  141. index: i
  142. }, undefined);
  143. }
  144. }
  145. resources[i] = resource;
  146. }
  147. var reflectedResources = new Array(resources.length);
  148. for (var i = 0; i < reflectedResources.length; ++i) {
  149. reflectedResources[i] = Promise.resolve(resources[i]).reflect();
  150. }
  151. var resultPromise = Promise.all(reflectedResources)
  152. .then(function(inspections) {
  153. for (var i = 0; i < inspections.length; ++i) {
  154. var inspection = inspections[i];
  155. if (inspection.isRejected()) {
  156. errorObj.e = inspection.error();
  157. return errorObj;
  158. } else if (!inspection.isFulfilled()) {
  159. resultPromise.cancel();
  160. return;
  161. }
  162. inspections[i] = inspection.value();
  163. }
  164. promise._pushContext();
  165. fn = tryCatch(fn);
  166. var ret = spreadArgs
  167. ? fn.apply(undefined, inspections) : fn(inspections);
  168. var promiseCreated = promise._popContext();
  169. debug.checkForgottenReturns(
  170. ret, promiseCreated, "Promise.using", promise);
  171. return ret;
  172. });
  173. var promise = resultPromise.lastly(function() {
  174. var inspection = new Promise.PromiseInspection(resultPromise);
  175. return dispose(resources, inspection);
  176. });
  177. resources.promise = promise;
  178. promise._setOnCancel(resources);
  179. return promise;
  180. };
  181. Promise.prototype._setDisposable = function (disposer) {
  182. this._bitField = this._bitField | 131072;
  183. this._disposer = disposer;
  184. };
  185. Promise.prototype._isDisposable = function () {
  186. return (this._bitField & 131072) > 0;
  187. };
  188. Promise.prototype._getDisposer = function () {
  189. return this._disposer;
  190. };
  191. Promise.prototype._unsetDisposable = function () {
  192. this._bitField = this._bitField & (~131072);
  193. this._disposer = undefined;
  194. };
  195. Promise.prototype.disposer = function (fn) {
  196. if (typeof fn === "function") {
  197. return new FunctionDisposer(fn, this, createContext());
  198. }
  199. throw new TypeError();
  200. };
  201. };