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