babylon.promise.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. module BABYLON {
  2. enum PromiseStates {
  3. Pending,
  4. Fulfilled,
  5. Rejected
  6. }
  7. class FulFillmentAgregator<T> {
  8. public count = 0;
  9. public target = 0;
  10. public rootPromise: InternalPromise<T>;
  11. public results: any[] = [];
  12. }
  13. class InternalPromise<T> {
  14. private _state = PromiseStates.Pending;
  15. private _result?: Nullable<T>;
  16. private _reason: any;
  17. private _children = new Array<InternalPromise<T>>();
  18. private _onFulfilled?: (fulfillment?: Nullable<T>) => Nullable<InternalPromise<T>> | T;
  19. private _onRejected?: (reason: any) => void;
  20. private _rejectWasConsumed = false;
  21. public get state(): PromiseStates {
  22. return this._state;
  23. }
  24. public get isFulfilled(): boolean {
  25. return this._state === PromiseStates.Fulfilled;
  26. }
  27. public get isRejected(): boolean {
  28. return this._state === PromiseStates.Rejected;
  29. }
  30. public get isPending(): boolean {
  31. return this._state === PromiseStates.Pending;
  32. }
  33. public value(): Nullable<T> | undefined {
  34. if (!this.isFulfilled) {
  35. throw new Error("Promise is not fulfilled");
  36. }
  37. return this._result;
  38. }
  39. public reason(): any {
  40. if (!this.isRejected) {
  41. throw new Error("Promise is not rejected");
  42. }
  43. return this._reason;
  44. }
  45. public constructor(resolver?: (
  46. resolve: (value?: Nullable<T>) => void,
  47. reject: (reason: any) => void
  48. ) => void) {
  49. if (!resolver) {
  50. return;
  51. }
  52. try {
  53. resolver((value?: Nullable<T>) => {
  54. this._resolve(value);
  55. }, (reason: any) => {
  56. this._reject(reason);
  57. });
  58. } catch (e) {
  59. this._reject(e);
  60. }
  61. }
  62. public catch(onRejected: (reason: any) => void): InternalPromise<T> {
  63. return this.then(undefined, onRejected);
  64. }
  65. public then(onFulfilled?: (fulfillment?: Nullable<T>) => Nullable<InternalPromise<T>> | T, onRejected?: (reason: any) => void): InternalPromise<T> {
  66. let newPromise = new InternalPromise<T>();
  67. newPromise._onFulfilled = onFulfilled;
  68. newPromise._onRejected = onRejected;
  69. // Composition
  70. this._children.push(newPromise);
  71. if (this._state !== PromiseStates.Pending) {
  72. if (this._state === PromiseStates.Fulfilled || this._rejectWasConsumed) {
  73. let returnedValue = newPromise._resolve(this._result);
  74. if (returnedValue !== undefined && returnedValue !== null) {
  75. if ((<InternalPromise<T>>returnedValue)._state !== undefined) {
  76. let returnedPromise = returnedValue as InternalPromise<T>;
  77. newPromise._children.push(returnedPromise);
  78. newPromise = returnedPromise;
  79. } else {
  80. newPromise._result = (<T>returnedValue);
  81. }
  82. }
  83. } else {
  84. newPromise._reject(this._reason);
  85. }
  86. }
  87. return newPromise;
  88. }
  89. private _moveChildren(children: InternalPromise<T>[]): void {
  90. this._children.push(...children.splice(0, children.length));
  91. if (this.isFulfilled) {
  92. for (var child of this._children) {
  93. child._resolve(this._result);
  94. }
  95. } else if (this.isRejected) {
  96. for (var child of this._children) {
  97. child._reject(this._reason);
  98. }
  99. }
  100. }
  101. private _resolve(value?: Nullable<T>): Nullable<InternalPromise<T>> | T {
  102. try {
  103. this._state = PromiseStates.Fulfilled;
  104. this._result = value;
  105. let returnedValue: Nullable<InternalPromise<T>> | T = null;
  106. if (this._onFulfilled) {
  107. returnedValue = this._onFulfilled(value);
  108. }
  109. if (returnedValue !== undefined && returnedValue !== null) {
  110. if ((<InternalPromise<T>>returnedValue)._state !== undefined) {
  111. // Transmit children
  112. let returnedPromise = returnedValue as InternalPromise<T>;
  113. returnedPromise._moveChildren(this._children);
  114. } else {
  115. value = <T>returnedValue;
  116. }
  117. }
  118. for (var child of this._children) {
  119. child._resolve(value);
  120. }
  121. return returnedValue;
  122. } catch (e) {
  123. this._reject(e);
  124. }
  125. return null;
  126. }
  127. private _reject(reason: any): void {
  128. this._state = PromiseStates.Rejected;
  129. this._reason = reason;
  130. if (this._onRejected) {
  131. this._onRejected(reason);
  132. this._rejectWasConsumed = true;
  133. }
  134. for (var child of this._children) {
  135. if (this._rejectWasConsumed) {
  136. child._resolve(null);
  137. } else {
  138. child._reject(reason);
  139. }
  140. }
  141. }
  142. public static resolve<T>(value: T): InternalPromise<T> {
  143. let newPromise = new InternalPromise<T>();
  144. newPromise._resolve(value);
  145. return newPromise;
  146. }
  147. private static _RegisterForFulfillment<T>(promise: InternalPromise<T>, agregator: FulFillmentAgregator<T[]>, index: number) {
  148. promise.then((value?: Nullable<T>) => {
  149. agregator.results[index] = value;
  150. agregator.count++;
  151. if (agregator.count === agregator.target) {
  152. agregator.rootPromise._resolve(agregator.results);
  153. }
  154. return null;
  155. }, (reason: any) => {
  156. if (!agregator.rootPromise.isRejected) {
  157. agregator.rootPromise._reject(reason);
  158. }
  159. })
  160. }
  161. public static all<T>(promises: InternalPromise<T>[]): InternalPromise<T[]> {
  162. let newPromise = new InternalPromise<T[]>();
  163. let agregator = new FulFillmentAgregator<T[]>();
  164. agregator.target = promises.length;
  165. agregator.rootPromise = newPromise;
  166. if (promises.length) {
  167. for(var index = 0; index < promises.length; index++) {
  168. InternalPromise._RegisterForFulfillment(promises[index], agregator, index);
  169. }
  170. } else {
  171. newPromise._resolve([]);
  172. }
  173. return newPromise;
  174. }
  175. }
  176. /**
  177. * Helper class that provides a small promise polyfill
  178. */
  179. export class PromisePolyfill {
  180. /**
  181. * Static function used to check if the polyfill is required
  182. * If this is the case then the function will inject the polyfill to window.Promise
  183. * @param force defines a boolean used to force the injection (mostly for testing purposes)
  184. */
  185. public static Apply(force = false): void {
  186. if (force || typeof Promise === 'undefined') {
  187. let root: any = window;
  188. root.Promise = InternalPromise;
  189. }
  190. }
  191. }
  192. }