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