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