babylon.promise.ts 6.4 KB

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