using.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. "use strict";
  2. module.exports = function (Promise, apiRejection, tryConvertToPromise,
  3. createContext) {
  4. var TypeError = require("./errors.js").TypeError;
  5. var inherits = require("./util.js").inherits;
  6. var PromiseInspection = Promise.PromiseInspection;
  7. function inspectionMapper(inspections) {
  8. var len = inspections.length;
  9. for (var i = 0; i < len; ++i) {
  10. var inspection = inspections[i];
  11. if (inspection.isRejected()) {
  12. return Promise.reject(inspection.error());
  13. }
  14. inspections[i] = inspection._settledValue;
  15. }
  16. return inspections;
  17. }
  18. function thrower(e) {
  19. setTimeout(function(){throw e;}, 0);
  20. }
  21. function castPreservingDisposable(thenable) {
  22. var maybePromise = tryConvertToPromise(thenable);
  23. if (maybePromise !== thenable &&
  24. typeof thenable._isDisposable === "function" &&
  25. typeof thenable._getDisposer === "function" &&
  26. thenable._isDisposable()) {
  27. maybePromise._setDisposable(thenable._getDisposer());
  28. }
  29. return maybePromise;
  30. }
  31. function dispose(resources, inspection) {
  32. var i = 0;
  33. var len = resources.length;
  34. var ret = Promise.defer();
  35. function iterator() {
  36. if (i >= len) return ret.resolve();
  37. var maybePromise = castPreservingDisposable(resources[i++]);
  38. if (maybePromise instanceof Promise &&
  39. maybePromise._isDisposable()) {
  40. try {
  41. maybePromise = tryConvertToPromise(
  42. maybePromise._getDisposer().tryDispose(inspection),
  43. resources.promise);
  44. } catch (e) {
  45. return thrower(e);
  46. }
  47. if (maybePromise instanceof Promise) {
  48. return maybePromise._then(iterator, thrower,
  49. null, null, null);
  50. }
  51. }
  52. iterator();
  53. }
  54. iterator();
  55. return ret.promise;
  56. }
  57. function disposerSuccess(value) {
  58. var inspection = new PromiseInspection();
  59. inspection._settledValue = value;
  60. inspection._bitField = 268435456;
  61. return dispose(this, inspection).thenReturn(value);
  62. }
  63. function disposerFail(reason) {
  64. var inspection = new PromiseInspection();
  65. inspection._settledValue = reason;
  66. inspection._bitField = 134217728;
  67. return dispose(this, inspection).thenThrow(reason);
  68. }
  69. function Disposer(data, promise, context) {
  70. this._data = data;
  71. this._promise = promise;
  72. this._context = context;
  73. }
  74. Disposer.prototype.data = function () {
  75. return this._data;
  76. };
  77. Disposer.prototype.promise = function () {
  78. return this._promise;
  79. };
  80. Disposer.prototype.resource = function () {
  81. if (this.promise().isFulfilled()) {
  82. return this.promise().value();
  83. }
  84. return null;
  85. };
  86. Disposer.prototype.tryDispose = function(inspection) {
  87. var resource = this.resource();
  88. var context = this._context;
  89. if (context !== undefined) context._pushContext();
  90. var ret = resource !== null
  91. ? this.doDispose(resource, inspection) : null;
  92. if (context !== undefined) context._popContext();
  93. this._promise._unsetDisposable();
  94. this._data = null;
  95. return ret;
  96. };
  97. Disposer.isDisposer = function (d) {
  98. return (d != null &&
  99. typeof d.resource === "function" &&
  100. typeof d.tryDispose === "function");
  101. };
  102. function FunctionDisposer(fn, promise, context) {
  103. this.constructor$(fn, promise, context);
  104. }
  105. inherits(FunctionDisposer, Disposer);
  106. FunctionDisposer.prototype.doDispose = function (resource, inspection) {
  107. var fn = this.data();
  108. return fn.call(resource, resource, inspection);
  109. };
  110. function maybeUnwrapDisposer(value) {
  111. if (Disposer.isDisposer(value)) {
  112. this.resources[this.index]._setDisposable(value);
  113. return value.promise();
  114. }
  115. return value;
  116. }
  117. Promise.using = function () {
  118. var len = arguments.length;
  119. if (len < 2) return apiRejection(
  120. "you must pass at least 2 arguments to Promise.using");
  121. var fn = arguments[len - 1];
  122. if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
  123. len--;
  124. var resources = new Array(len);
  125. for (var i = 0; i < len; ++i) {
  126. var resource = arguments[i];
  127. if (Disposer.isDisposer(resource)) {
  128. var disposer = resource;
  129. resource = resource.promise();
  130. resource._setDisposable(disposer);
  131. } else {
  132. var maybePromise = tryConvertToPromise(resource);
  133. if (maybePromise instanceof Promise) {
  134. resource =
  135. maybePromise._then(maybeUnwrapDisposer, null, null, {
  136. resources: resources,
  137. index: i
  138. }, undefined);
  139. }
  140. }
  141. resources[i] = resource;
  142. }
  143. var promise = Promise.settle(resources)
  144. .then(inspectionMapper)
  145. .then(function(vals) {
  146. promise._pushContext();
  147. var ret;
  148. try {
  149. ret = fn.apply(undefined, vals);
  150. } finally {
  151. promise._popContext();
  152. }
  153. return ret;
  154. })
  155. ._then(
  156. disposerSuccess, disposerFail, undefined, resources, undefined);
  157. resources.promise = promise;
  158. return promise;
  159. };
  160. Promise.prototype._setDisposable = function (disposer) {
  161. this._bitField = this._bitField | 262144;
  162. this._disposer = disposer;
  163. };
  164. Promise.prototype._isDisposable = function () {
  165. return (this._bitField & 262144) > 0;
  166. };
  167. Promise.prototype._getDisposer = function () {
  168. return this._disposer;
  169. };
  170. Promise.prototype._unsetDisposable = function () {
  171. this._bitField = this._bitField & (~262144);
  172. this._disposer = undefined;
  173. };
  174. Promise.prototype.disposer = function (fn) {
  175. if (typeof fn === "function") {
  176. return new FunctionDisposer(fn, this, createContext());
  177. }
  178. throw new TypeError();
  179. };
  180. };