1
1
export type MaybePromise < T > = Promise < T > | T ;
2
2
export type MaybePromiseLike < T > = PromiseLike < T > | T ;
3
3
4
+ const FAKE_PROMISE_SYMBOL_NAME = '@whatwg-node/promise-helpers/FakePromise' ;
5
+
4
6
export function isPromise < T > ( value : MaybePromise < T > ) : value is Promise < T > ;
5
7
export function isPromise < T > ( value : MaybePromiseLike < T > ) : value is PromiseLike < T > ;
6
8
export function isPromise < T > ( value : MaybePromiseLike < T > ) : value is PromiseLike < T > {
7
9
return ( value as any ) ?. then != null ;
8
10
}
9
11
12
+ export function isActualPromise < T > ( value : MaybePromiseLike < T > ) : value is Promise < T > {
13
+ const maybePromise = value as any ;
14
+ return maybePromise && maybePromise . then && maybePromise . catch && maybePromise . finally ;
15
+ }
16
+
10
17
export function handleMaybePromise < TInput , TOutput > (
11
18
inputFactory : ( ) => MaybePromise < TInput > ,
12
19
outputSuccessFactory : ( value : TInput ) => MaybePromise < TOutput > ,
13
20
outputErrorFactory ?: ( err : any ) => MaybePromise < TOutput > ,
21
+ finallyFactory ?: ( ) => MaybePromise < void > ,
14
22
) : MaybePromise < TOutput > ;
15
23
export function handleMaybePromise < TInput , TOutput > (
16
24
inputFactory : ( ) => MaybePromiseLike < TInput > ,
17
25
outputSuccessFactory : ( value : TInput ) => MaybePromiseLike < TOutput > ,
18
26
outputErrorFactory ?: ( err : any ) => MaybePromiseLike < TOutput > ,
27
+ finallyFactory ?: ( ) => MaybePromiseLike < void > ,
19
28
) : MaybePromiseLike < TOutput > ;
20
29
export function handleMaybePromise < TInput , TOutput > (
21
30
inputFactory : ( ) => MaybePromiseLike < TInput > ,
22
31
outputSuccessFactory : ( value : TInput ) => MaybePromiseLike < TOutput > ,
23
32
outputErrorFactory ?: ( err : any ) => MaybePromiseLike < TOutput > ,
33
+ finallyFactory ?: ( ) => MaybePromiseLike < void > ,
24
34
) : MaybePromiseLike < TOutput > {
25
- function _handleMaybePromise ( ) {
26
- const input$ = inputFactory ( ) ;
27
- if ( isFakePromise < TInput > ( input$ ) ) {
28
- return outputSuccessFactory ( input$ . __fakePromiseValue ) ;
29
- }
30
- if ( isFakeRejectPromise ( input$ ) ) {
31
- throw input$ . __fakeRejectError ;
32
- }
33
- if ( isPromise ( input$ ) ) {
34
- return input$ . then ( outputSuccessFactory , outputErrorFactory ) ;
35
- }
36
- return outputSuccessFactory ( input$ ) ;
37
- }
38
- if ( ! outputErrorFactory ) {
39
- return _handleMaybePromise ( ) ;
40
- }
41
- try {
42
- return _handleMaybePromise ( ) ;
43
- } catch ( err ) {
44
- return outputErrorFactory ( err ) ;
35
+ let result$ = fakePromise ( ) . then ( inputFactory ) . then ( outputSuccessFactory , outputErrorFactory ) ;
36
+
37
+ if ( finallyFactory ) {
38
+ result$ = result$ . finally ( finallyFactory ) ;
45
39
}
40
+
41
+ return unfakePromise ( result$ ) ;
46
42
}
47
43
48
- export function fakePromise < T > ( value : T ) : Promise < Awaited < T > > ;
44
+ export function fakePromise < T > ( value : MaybePromise < T > ) : Promise < T > ;
45
+ export function fakePromise < T > ( value : MaybePromiseLike < T > ) : Promise < T > ;
49
46
export function fakePromise ( value : void ) : Promise < void > ;
50
- export function fakePromise < T = void > ( value : T ) : Promise < T > {
51
- if ( isPromise ( value ) ) {
47
+ export function fakePromise < T > ( value : MaybePromiseLike < T > ) : Promise < T > {
48
+ if ( value && isActualPromise ( value ) ) {
52
49
return value ;
53
50
}
51
+
52
+ if ( isPromise ( value ) ) {
53
+ return {
54
+ then : ( resolve , reject ) => fakePromise ( value . then ( resolve , reject ) ) ,
55
+ catch : reject => fakePromise ( value . then ( res => res , reject ) ) ,
56
+ finally : cb => fakePromise ( cb ? promiseLikeFinally ( value , cb ) : value ) ,
57
+ [ Symbol . toStringTag ] : 'Promise' ,
58
+ } ;
59
+ }
60
+
54
61
// Write a fake promise to avoid the promise constructor
55
62
// being called with `new Promise` in the browser.
56
63
return {
57
- then ( resolve : ( value : T ) => any ) {
64
+ then ( resolve ) {
58
65
if ( resolve ) {
59
- const callbackResult = resolve ( value ) ;
60
- if ( isPromise ( callbackResult ) ) {
61
- return callbackResult ;
66
+ try {
67
+ return fakePromise ( resolve ( value ) ) ;
68
+ } catch ( err ) {
69
+ return fakeRejectPromise ( err ) ;
62
70
}
63
- return fakePromise ( callbackResult ) ;
64
71
}
65
72
return this ;
66
73
} ,
@@ -69,19 +76,20 @@ export function fakePromise<T = void>(value: T): Promise<T> {
69
76
} ,
70
77
finally ( cb ) {
71
78
if ( cb ) {
72
- const callbackResult = cb ( ) ;
73
- if ( isPromise ( callbackResult ) ) {
74
- return callbackResult . then (
79
+ try {
80
+ return fakePromise ( cb ( ) ) . then (
75
81
( ) => value ,
76
82
( ) => value ,
77
83
) ;
84
+ } catch ( err ) {
85
+ return fakeRejectPromise ( err ) ;
78
86
}
79
- return fakePromise ( value ) ;
80
87
}
81
88
return this ;
82
89
} ,
83
90
[ Symbol . toStringTag ] : 'Promise' ,
84
91
__fakePromiseValue : value ,
92
+ [ Symbol . for ( FAKE_PROMISE_SYMBOL_NAME ) ] : 'resolved' ,
85
93
} as Promise < T > ;
86
94
}
87
95
@@ -155,28 +163,41 @@ export function iterateAsync<TInput, TOutput>(
155
163
return iterate ( ) ;
156
164
}
157
165
158
- export function fakeRejectPromise ( error : unknown ) : Promise < never > {
159
- if ( isPromise ( error ) ) {
160
- return error as Promise < never > ;
161
- }
166
+ export function fakeRejectPromise < T > ( error : unknown ) : Promise < T > {
162
167
return {
163
- then ( ) {
168
+ then ( _resolve , reject ) {
169
+ if ( reject ) {
170
+ try {
171
+ return fakePromise ( reject ( error ) ) ;
172
+ } catch ( err ) {
173
+ return fakeRejectPromise ( err ) ;
174
+ }
175
+ }
164
176
return this ;
165
177
} ,
166
178
catch ( reject : ( error : unknown ) => any ) {
167
179
if ( reject ) {
168
- return fakePromise ( reject ( error ) ) ;
180
+ try {
181
+ return fakePromise ( reject ( error ) ) ;
182
+ } catch ( err ) {
183
+ return fakeRejectPromise ( err ) ;
184
+ }
169
185
}
170
186
return this ;
171
187
} ,
172
188
finally ( cb ) {
173
189
if ( cb ) {
174
- cb ( ) ;
190
+ try {
191
+ cb ( ) ;
192
+ } catch ( err ) {
193
+ return fakeRejectPromise ( err ) ;
194
+ }
175
195
}
176
196
return this ;
177
197
} ,
178
198
__fakeRejectError : error ,
179
199
[ Symbol . toStringTag ] : 'Promise' ,
200
+ [ Symbol . for ( FAKE_PROMISE_SYMBOL_NAME ) ] : 'rejected' ,
180
201
} as Promise < never > ;
181
202
}
182
203
@@ -294,9 +315,47 @@ function iteratorResult<T>(value: T): IteratorResult<T> {
294
315
}
295
316
296
317
function isFakePromise < T > ( value : any ) : value is Promise < T > & { __fakePromiseValue : T } {
297
- return ( value as any ) ?. __fakePromiseValue != null ;
318
+ return ( value as any ) ?. [ Symbol . for ( FAKE_PROMISE_SYMBOL_NAME ) ] === 'resolved' ;
298
319
}
299
320
300
321
function isFakeRejectPromise ( value : any ) : value is Promise < never > & { __fakeRejectError : any } {
301
- return ( value as any ) ?. __fakeRejectError != null ;
322
+ return ( value as any ) ?. [ Symbol . for ( FAKE_PROMISE_SYMBOL_NAME ) ] === 'rejected' ;
323
+ }
324
+
325
+ export function promiseLikeFinally < T > (
326
+ value : PromiseLike < T > | Promise < T > ,
327
+ onFinally : ( ) => MaybePromiseLike < void > ,
328
+ ) : PromiseLike < T > {
329
+ if ( 'finally' in value ) {
330
+ return value . finally ( onFinally ) ;
331
+ }
332
+
333
+ return value . then (
334
+ res => {
335
+ const finallyRes = onFinally ( ) ;
336
+ return isPromise ( finallyRes ) ? finallyRes . then ( ( ) => res ) : res ;
337
+ } ,
338
+ err => {
339
+ const finallyRes = onFinally ( ) ;
340
+ if ( isPromise ( finallyRes ) ) {
341
+ return finallyRes . then ( ( ) => {
342
+ throw err ;
343
+ } ) ;
344
+ } else {
345
+ throw err ;
346
+ }
347
+ } ,
348
+ ) ;
349
+ }
350
+
351
+ export function unfakePromise < T > ( promise : Promise < T > ) : MaybePromise < T > {
352
+ if ( isFakePromise < T > ( promise ) ) {
353
+ return promise . __fakePromiseValue ;
354
+ }
355
+
356
+ if ( isFakeRejectPromise ( promise ) ) {
357
+ throw promise . __fakeRejectError ;
358
+ }
359
+
360
+ return promise ;
302
361
}
0 commit comments