1/* 2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16package std.core; 17 18/** 19 * Interface for objects with 'then' method. 20 */ 21export interface PromiseLike<out T> { 22 then<U, E = never>(onFulfilled?: (value: T) => U|PromiseLike<U>, 23 onRejected?: (error: NullishType) => E|PromiseLike<E>): PromiseLike<U|E>; 24} 25 26/** 27 * Base class for Promise.allSettled return type. 28 */ 29class PromiseSettledResultBase { 30 status: string = ""; 31} 32 33/** 34 * The class describes fulfillment result of Promise.allSettled's argument. 35 */ 36export class PromiseFulfilledResult<T> extends PromiseSettledResultBase { 37 value: T; 38} 39 40/** 41 * The class describes rejection result of Promise.allSettled's argument. 42 */ 43export class PromiseRejectedResult extends PromiseSettledResultBase { 44 reason: NullishType; 45} 46 47export type PromiseSettledResult<T> = PromiseFulfilledResult<T>|PromiseRejectedResult 48 49/** 50 * Class represents a result of an asynchronous operation in the future. 51 */ 52export final class Promise<out T> implements PromiseLike<T> { 53 private constructor() { 54 } 55 56 constructor(callback: (resolve: (value: T|PromiseLike<T>) => void) => void) { 57 try { 58 callback((value: T|PromiseLike<T>): void => { 59 this.doResolve<T>(value); 60 }); 61 } catch (error) { 62 this.rejectImpl(error); 63 } 64 } 65 66 constructor(callback: (resolve: (value: T|PromiseLike<T>) => void, 67 reject: (error: NullishType) => void) => void) { 68 try { 69 callback((value: T|PromiseLike<T>): void => { 70 this.doResolve<T>(value); 71 }, (error: NullishType): void => { 72 this.rejectImpl(error); 73 }); 74 } catch (error) { 75 this.rejectImpl(error); 76 } 77 } 78 79 then<U>(onFulfilled: () => U|PromiseLike<U>): Promise<U> { 80 let promise = new Promise<U>(); 81 let fn: () => void = (): void => { 82 try { 83 if (this.state == Promise.STATE_RESOLVED) { 84 promise.doResolve<U>(onFulfilled()); 85 } else { 86 promise.rejectImpl(this.value); 87 } 88 } catch (error) { 89 promise.rejectImpl(error); 90 } 91 } 92 this.submitCallback(fn as Object); 93 return promise; 94 } 95 96 then<U>(onFulfilled: (value: T) => U|PromiseLike<U>): Promise<U> { 97 let promise = new Promise<U>(); 98 let fn: () => void = (): void => { 99 try { 100 if (this.state == Promise.STATE_RESOLVED) { 101 promise.doResolve<U>(onFulfilled(this.value!)); 102 } else { 103 promise.rejectImpl(this.value! as Object); 104 } 105 } catch (e) { 106 promise.rejectImpl(e); 107 } 108 } 109 this.submitCallback(fn as Object); 110 return promise; 111 } 112 113 then<U, E = never>(onFulfilled?: (value: T) => U|PromiseLike<U>, 114 onRejected?: (error: NullishType) => E|PromiseLike<E>): Promise<U|E> { 115 let promise = new Promise<U|E>(); 116 let fn: () => void = (): void => { 117 try { 118 if (this.state == Promise.STATE_RESOLVED) { 119 if (onFulfilled) { 120 promise.doResolve<U>(onFulfilled(this.value!)); 121 } else { 122 promise.resolveImpl(this.value!); 123 } 124 } else { 125 if (onRejected) { 126 promise.rejectImpl(onRejected(this.value! as Object)); 127 } else { 128 promise.rejectImpl(this.value!); 129 } 130 } 131 } catch (e) { 132 promise.rejectImpl(e); 133 } 134 } 135 this.submitCallback(fn as Object); 136 return promise; 137 } 138 139 // NOTE(audovichenko): Add '?' to onRejection param. Issue #20001 140 catch<U = never>(onRejected: () => U|PromiseLike<U>): Promise<T|U> { 141 let promise = new Promise<T|U>(); 142 let fn: () => void = (): void => { 143 try { 144 if (this.state == Promise.STATE_REJECTED) { 145 let res: U|PromiseLike<U> = onRejected(); 146 promise.doResolve<U>(res); 147 } else { 148 promise.doResolve<T>(this.value!); 149 } 150 } catch (e) { 151 promise.rejectImpl(e); 152 } 153 } 154 this.submitCallback(fn as Object); 155 return promise; 156 } 157 158 catch<U = never>(onRejected?: (error: NullishType) => U|PromiseLike<U>): Promise<T|U> { 159 let promise = new Promise<T|U>(); 160 let fn: () => void = (): void => { 161 try { 162 if (this.state == Promise.STATE_REJECTED) { 163 if (onRejected) { 164 let res: U|PromiseLike<U> = onRejected(this.value); 165 promise.doResolve<U>(res); 166 } else { 167 promise.rejectImpl(this.value!); 168 } 169 } else { 170 promise.doResolve<T>(this.value!); 171 } 172 } catch (e) { 173 promise.rejectImpl(e); 174 } 175 } 176 this.submitCallback(fn as Object); 177 return promise; 178 } 179 180 finally(onFinally?: () => void): Promise<T> { 181 let promise = new Promise<T>(); 182 let fn: () => void = (): void => { 183 try { 184 if (onFinally) { 185 try { 186 onFinally(); 187 } catch(error) { 188 promise.rejectImpl(error); 189 } 190 } 191 if (this.state == Promise.STATE_RESOLVED) { 192 promise.resolveImpl(this.value!); 193 } else { 194 promise.rejectImpl(this.value! as Object); 195 } 196 } catch (e) { 197 promise.rejectImpl(e); 198 } 199 } 200 this.submitCallback(fn as Object); 201 return promise; 202 } 203 204 static resolve(): Promise<void> { 205 let p = new Promise<void>(); 206 p.resolveImpl(undefined); 207 return p; 208 } 209 210 static resolve<U>(value: U|PromiseLike<U>): Promise<U> { 211 if (value instanceof Promise) { 212 return value as Promise<U>; 213 } 214 let p = new Promise<U>(); 215 p.doResolve(value); 216 return p; 217 } 218 219 static reject(): Promise<void> { 220 let p = new Promise<void>(); 221 p.rejectImpl(null); 222 return p; 223 } 224 225 static reject<U = never>(value: NullishType): Promise<U> { 226 let p = new Promise<U>(); 227 p.rejectImpl(value); 228 return p; 229 } 230 231 static all<U>(promises: (U|PromiseLike<U>)[]): Promise<Array<U>> { 232 return new Promise<Array<U>>((resolve: (value: Array<U>) => void, reject: (error: NullishType) => void): void => { 233 new AllPromiseConcurrency<U>(resolve, reject).all(promises); 234 }); 235 } 236 237 static all<U>(promises: Iterable<U|PromiseLike<U>>): Promise<Array<U>> { 238 try { 239 return Promise.all<U>(Promise.toArray(promises)); 240 } catch (e: Error) { 241 return Promise.reject<Array<U>>(e); 242 } 243 } 244 245 static allSettled<U>(promises: (U|PromiseLike<U>)[]): Promise<PromiseSettledResult<U>[]> { 246 return new Promise<PromiseSettledResult<U>[]>((resolve: (value: PromiseSettledResult<U>[]) => void, reject: (error: NullishType) => void): void => { 247 // This temp object is needed because es2panda cannot change captured primitives 248 new AllSettledPromiseConcurrency<U>(resolve, reject).allSettled(promises); 249 }); 250 } 251 252 static allSettled<U>(promises: Iterable<U|PromiseLike<U>>): Promise<PromiseSettledResult<U>[]> { 253 try { 254 return Promise.allSettled<U>(Promise.toArray(promises)); 255 } catch (e: Error) { 256 return Promise.reject<PromiseSettledResult<U>[]>(e); 257 } 258 } 259 260 static any<U>(promises: (U|PromiseLike<U>)[]): Promise<U> { 261 return new Promise<U>((resolve: (value: U) => void, reject: (error: NullishType) => void): void => { 262 // This temp object is needed because es2panda cannot change captured primitives 263 new AnyPromiseConcurrency<U>(resolve, reject).any(promises); 264 }); 265 } 266 267 static any<U>(promises: Iterable<U|PromiseLike<U>>): Promise<U> { 268 try { 269 return Promise.any<U>(Promise.toArray(promises)); 270 } catch (e: Error) { 271 return Promise.reject<U>(e); 272 } 273 } 274 275 static race<U>(promises: (U|PromiseLike<U>)[]): Promise<U> { 276 return new Promise<U>((resolve: (value: U) => void, reject: (error: NullishType) => void): void => { 277 // This temp object is needed because es2panda cannot change captured primitives 278 new RacePromiseConcurrency<U>(resolve, reject).race(promises); 279 }); 280 } 281 282 static race<U>(promises: Iterable<U|PromiseLike<U>>): Promise<U> { 283 try { 284 return Promise.race<U>(Promise.toArray(promises)); 285 } catch (e: Error) { 286 return Promise.reject<U>(e); 287 } 288 } 289 290 private static toArray<U>(values: Iterable<U>): U[] { 291 let it = values.$_iterator(); 292 let v = it.next(); 293 if (v.done) { 294 return new U[0]; 295 } 296 let array: U[] = new U[1]; 297 array[0] = v.value!; 298 let len = 1; 299 while (true) { 300 v = it.next(); 301 if (v.done) { 302 return Promise.trim(array, len); 303 } 304 array = Promise.ensureCapacity(array, len + 1); 305 array[len] = v.value!; 306 ++len; 307 } 308 } 309 310 private static trim<U>(array: U[], length: int): U[] { 311 if (array.length == length) { 312 return array; 313 } 314 let result = new U[length]; 315 for (let i = 0; i < length; ++i) { 316 result[i] = array[i]; 317 } 318 return result; 319 } 320 321 private static ensureCapacity<U>(queue: U[], size: int): U[] { 322 if (size <= queue.length) { 323 return queue; 324 } 325 let newQueue = new U[size * 2]; 326 for (let i = 0; i < queue.length; ++i) { 327 newQueue[i] = queue[i]; 328 } 329 return newQueue; 330 } 331 332 private subscribeOnAnotherPromise<U>(internalPromise: PromiseLike<U>): void { 333 let thisPromise = this; 334 internalPromise.then<void, void>((value: U): void => { 335 thisPromise.resolveImpl(value); 336 }, (error: NullishType): void => { 337 thisPromise.rejectImpl(error); 338 }); 339 } 340 341 private doResolve<U>(value: U|PromiseLike<U>): void { 342 if (value instanceof PromiseLike) { 343 this.subscribeOnAnotherPromise<U>(value as PromiseLike<U>); 344 } else { 345 this.resolveImpl(value); 346 } 347 } 348 349 public native awaitResolution(): T; 350 351 private native resolveImpl<U>(value: U): void; 352 private native rejectImpl(error: NullishType): void; 353 private native submitCallback(callback: Object): void; 354 355 private static STATE_PENDING = 0; 356 private static STATE_RESOLVED = 1; 357 private static STATE_REJECTED = 2; 358 private static EMPTY_QUEUE = new Object[0]; 359 360 // Order of fields should be the following 361 // 1. Reference fields 362 // 2. Primitive fields in mostly size decreasing order 363 // filling alignment holes 364 private value: T | null = null; 365 private mutex = new Mutex(); 366 private event = new Event(); 367 private callbackQueue: Object[] | null = null; 368 private launchModeQueue: int[] | null = null; 369 private interopObject: Object|null = null; 370 private linkedPromise: Object | null = null; 371 private queueSize: int = 0; 372 private remotePromiseResolver: long = 0; 373 private state: int = Promise.STATE_PENDING; 374} 375 376class PromiseConcurrencyBase<T> { 377 toPromiseLike(value: T|PromiseLike<T>): PromiseLike<T> { 378 if (value instanceof PromiseLike) { 379 let p = value as PromiseLike<T>; 380 return p; 381 } else { 382 let p = Promise.resolve<T>(value as T); 383 return p; 384 } 385 } 386} 387 388class AllPromiseConcurrency<T> extends PromiseConcurrencyBase<T> { 389 constructor(resolve: (value: Array<T>) => void, reject: (error: NullishType) => void) { 390 this.resolve = resolve; 391 this.reject = reject; 392 } 393 394 all(promises: (T|PromiseLike<T>)[]): void { 395 if (promises.length == 0) { 396 this.resolve(this.values); 397 return; 398 } else { 399 this.values = new Array<T>(promises.length); 400 } 401 for (let i = 0; i < promises.length; ++i) { 402 let idx = i; 403 this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => { 404 this.resolveImpl(value, idx); 405 }, (error: NullishType): void => { 406 this.reject(error); 407 }); 408 } 409 } 410 411 private resolveImpl(value: T, idx: int): void { 412 this.values[idx] = value; 413 ++this.resolvedCnt; 414 if (this.resolvedCnt == this.values!.length) { 415 this.resolve(this.values!); 416 } 417 } 418 419 private resolvedCnt = 0; 420 private rejectedCnt = 0; 421 private values: Array<T> = new Array<T>(); 422 private resolve: (value: Array<T>) => void; 423 private reject: (error: NullishType) => void; 424} 425 426class AllSettledPromiseConcurrency<T> extends PromiseConcurrencyBase<T> { 427 constructor(resolve: (value: PromiseSettledResult<T>[]) => void, reject: (error: NullishType) => void) { 428 this.resolve = resolve; 429 this.reject = reject; 430 } 431 432 allSettled(promises: (T|PromiseLike<T>)[]): void { 433 this.values = new PromiseSettledResultBase[promises.length]; 434 if (promises.length == 0) { 435 this.resolve(this.getValues()); 436 return; 437 } 438 for (let i = 0; i < promises.length; ++i) { 439 let idx = i; 440 this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => { 441 this.resolveImpl(value as T, idx); 442 }, (error: NullishType): void => { 443 this.rejectImpl(error, idx); 444 }); 445 } 446 } 447 448 private resolveImpl(value: T, idx: int): void { 449 let res = new PromiseFulfilledResult<T>(); 450 res.status = "fulfilled"; 451 res.value = value; 452 this.values![idx] = res; 453 ++this.resolvedCnt; 454 if (this.resolvedCnt == this.values!.length) { 455 this.resolve(this.getValues()); 456 } 457 } 458 459 private rejectImpl(error: NullishType, idx: int): void { 460 let res = new PromiseRejectedResult(); 461 res.status = "rejected"; 462 res.reason = error; 463 this.values![idx] = res; 464 ++this.resolvedCnt; 465 if (this.resolvedCnt == this.values!.length) { 466 this.resolve(this.getValues()); 467 } 468 } 469 470 private getValues(): PromiseSettledResult<T>[] { 471 return (this.values as Object) as PromiseSettledResult<T>[]; 472 } 473 474 private resolvedCnt = 0; 475 private rejectedCnt = 0; 476 private values: PromiseSettledResultBase[]|null = null; 477 private resolve: (value: PromiseSettledResult<T>[]) => void; 478 private reject: (error: NullishType) => void; 479} 480 481class AnyPromiseConcurrency<T> extends PromiseConcurrencyBase<T> { 482 constructor(resolve: (value: T) => void, reject: (error: NullishType) => void) { 483 this.resolve = resolve; 484 this.reject = reject; 485 } 486 487 any(promises: (T|PromiseLike<T>)[]): void { 488 this.errors = new Error[promises.length]; 489 if (promises.length == 0) { 490 this.reject(new AggregateError(this.errors!, "All promises are rejected")); 491 return; 492 } 493 for (let i = 0; i < promises.length; ++i) { 494 let idx = i; 495 this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => { 496 this.resolveImpl(value as T); 497 }, (error: NullishType): void => { 498 this.rejectImpl(error, idx); 499 }); 500 } 501 } 502 503 private resolveImpl(value: T) { 504 ++this.resolvedCnt; 505 if (this.resolvedCnt == 1) { 506 this.resolve(value); 507 } 508 } 509 510 private rejectImpl(error: NullishType, idx: int) { 511 ++this.rejectedCnt; 512 if (error == null || error instanceof Error) { 513 this.errors![idx] = error as Error; 514 } else { 515 this.errors![idx] = new Error(error!.toString()); 516 } 517 if (this.rejectedCnt == this.errors!.length) { 518 this.reject(new AggregateError(this.errors!, "All promises are rejected")); 519 } 520 } 521 522 private resolvedCnt = 0; 523 private rejectedCnt = 0; 524 private errors: Error[]|null = null; 525 private resolve: (value: T) => void; 526 private reject: (error: NullishType) => void; 527} 528 529class RacePromiseConcurrency<T> extends PromiseConcurrencyBase<T> { 530 constructor(resolve: (value: T) => void, reject: (error: NullishType) => void) { 531 this.resolve = resolve; 532 this.reject = reject; 533 } 534 535 race(promises: (T|PromiseLike<T>)[]): void { 536 for (let i = 0; i < promises.length; ++i) { 537 let idx = i; 538 this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => { 539 this.resolveImpl(value as T); 540 }, (error: NullishType): void => { 541 this.rejectImpl(error); 542 }); 543 } 544 } 545 546 private resolveImpl(value: T) { 547 if (!this.settled) { 548 this.resolve(value) 549 this.settled = true; 550 } 551 } 552 553 private rejectImpl(error: NullishType) { 554 if (!this.settled) { 555 this.reject(error); 556 this.settled = true; 557 } 558 } 559 560 private settled = false; 561 private resolve: (value: T) => void; 562 private reject: (error: NullishType) => void; 563} 564 565export type NullablePromise<out T> = Promise<T> | null; 566