babylon.promise.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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 _child: 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 isFulfilled(): boolean {
  25. return this._state === PromiseStates.Fulfilled;
  26. }
  27. public isRejected(): boolean {
  28. return this._state === PromiseStates.Rejected;
  29. }
  30. public 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((<Error>e).message);
  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._child = 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._child = 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 _resolve(value?: Nullable<T>): Nullable<InternalPromise<T>> | T {
  90. try {
  91. this._state = PromiseStates.Fulfilled;
  92. this._result = value;
  93. let returnedPromise: Nullable<InternalPromise<T>> | T = null;
  94. if (this._onFulfilled) {
  95. returnedPromise = this._onFulfilled(value);
  96. }
  97. if (this._child) {
  98. this._child._resolve(value);
  99. }
  100. return returnedPromise;
  101. } catch(e) {
  102. this._reject((<Error>e).message);
  103. }
  104. return null;
  105. }
  106. private _reject(reason: any): void {
  107. this._state = PromiseStates.Rejected;
  108. this._reason = reason;
  109. if (this._onRejected) {
  110. this._onRejected(reason);
  111. this._rejectWasConsumed = true;
  112. }
  113. if (this._child) {
  114. if (this._rejectWasConsumed) {
  115. this._child._resolve(null);
  116. } else {
  117. this._child._reject(reason);
  118. }
  119. }
  120. }
  121. public static resolve<T>(value: T): InternalPromise<T> {
  122. let newPromise = new InternalPromise<T>();
  123. newPromise._resolve(value);
  124. return newPromise;
  125. }
  126. private static _RegisterForFulfillment<T>(promise: InternalPromise<T>, agregator: FulFillmentAgregator<T[]>, index: number) {
  127. promise.then((value?: Nullable<T>) => {
  128. agregator.results[index] = value;
  129. agregator.count++;
  130. if (agregator.count === agregator.target) {
  131. agregator.rootPromise._resolve(agregator.results);
  132. }
  133. return null;
  134. }, (reason: any) => {
  135. if (!agregator.rootPromise.isRejected) {
  136. agregator.rootPromise._reject(reason);
  137. }
  138. })
  139. }
  140. public static all<T>(promises: InternalPromise<T>[]): InternalPromise<T[]> {
  141. let newPromise = new InternalPromise<T[]>();
  142. let agregator = new FulFillmentAgregator<T[]>();
  143. agregator.target = promises.length;
  144. agregator.rootPromise = newPromise;
  145. for(var index = 0; index < promises.length; index++) {
  146. InternalPromise._RegisterForFulfillment(promises[index], agregator, index);
  147. }
  148. return newPromise;
  149. }
  150. }
  151. /**
  152. * Helper class that provides a small promise polyfill
  153. */
  154. export class PromisePolyfill {
  155. /**
  156. * Static function used to check if the polyfill is required
  157. * If this is the case then the function will inject the polyfill to window.Promise
  158. * @param force defines a boolean used to force the injection (mostly for testing purposes)
  159. */
  160. public static Apply(force = false): void {
  161. if (force || typeof Promise === 'undefined') {
  162. let root:any = window;
  163. root.Promise = InternalPromise;
  164. }
  165. }
  166. }
  167. }