@@ -53,18 +53,34 @@ export interface HttpResponseBody {
53
53
} ;
54
54
}
55
55
56
+ interface CancellablePromise < T > {
57
+ promise : Promise < T > ;
58
+ cancel : ( ) => void ;
59
+ }
60
+
56
61
/**
57
62
* Returns a Promise that will be rejected after the given duration.
58
63
* The error will be of type FunctionsError.
59
64
*
60
65
* @param millis Number of milliseconds to wait before rejecting.
61
66
*/
62
- function failAfter ( millis : number ) : Promise < never > {
63
- return new Promise ( ( _ , reject ) => {
64
- setTimeout ( ( ) => {
65
- reject ( new FunctionsError ( 'deadline-exceeded' , 'deadline-exceeded' ) ) ;
66
- } , millis ) ;
67
- } ) ;
67
+ function failAfter ( millis : number ) : CancellablePromise < never > {
68
+ // Node timers and browser timers are fundamentally incompatible, but we
69
+ // don't care about the value here
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ let timer : any | null = null ;
72
+ return {
73
+ promise : new Promise ( ( _ , reject ) => {
74
+ timer = setTimeout ( ( ) => {
75
+ reject ( new FunctionsError ( 'deadline-exceeded' , 'deadline-exceeded' ) ) ;
76
+ } , millis ) ;
77
+ } ) ,
78
+ cancel : ( ) => {
79
+ if ( timer ) {
80
+ clearTimeout ( timer ) ;
81
+ }
82
+ }
83
+ } ;
68
84
}
69
85
70
86
/**
@@ -247,12 +263,16 @@ async function call(
247
263
// Default timeout to 70s, but let the options override it.
248
264
const timeout = options . timeout || 70000 ;
249
265
266
+ const failAfterHandle = failAfter ( timeout ) ;
250
267
const response = await Promise . race ( [
251
268
postJSON ( url , body , headers , functionsInstance . fetchImpl ) ,
252
- failAfter ( timeout ) ,
269
+ failAfterHandle . promise ,
253
270
functionsInstance . cancelAllRequests
254
271
] ) ;
255
272
273
+ // Always clear the failAfter timeout
274
+ failAfterHandle . cancel ( ) ;
275
+
256
276
// If service was deleted, interrupted response throws an error.
257
277
if ( ! response ) {
258
278
throw new FunctionsError (
0 commit comments