• 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
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