• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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