using.js 7.3 KB

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