@@ -26,6 +26,12 @@ const {
2626 Promise,
2727} = primordials ;
2828
29+ const {
30+ codes : { ERR_INVALID_ARG_TYPE }
31+ } = require ( 'internal/errors' ) ;
32+
33+ let DOMException ;
34+
2935const {
3036 immediateInfo,
3137 toggleImmediateRef
@@ -118,6 +124,11 @@ function enroll(item, msecs) {
118124 * DOM-style timers
119125 */
120126
127+ function lazyDOMException ( message ) {
128+ if ( DOMException === undefined )
129+ DOMException = internalBinding ( 'messaging' ) . DOMException ;
130+ return new DOMException ( message ) ;
131+ }
121132
122133function setTimeout ( callback , after , arg1 , arg2 , arg3 ) {
123134 validateCallback ( callback ) ;
@@ -149,11 +160,40 @@ function setTimeout(callback, after, arg1, arg2, arg3) {
149160 return timeout ;
150161}
151162
152- setTimeout [ customPromisify ] = function ( after , value ) {
163+ setTimeout [ customPromisify ] = function ( after , value , options = { } ) {
153164 const args = value !== undefined ? [ value ] : value ;
154- return new Promise ( ( resolve ) => {
165+ if ( options == null || typeof options !== 'object' ) {
166+ return Promise . reject (
167+ new ERR_INVALID_ARG_TYPE (
168+ 'options' ,
169+ 'Object' ,
170+ options ) ) ;
171+ }
172+ const { signal } = options ;
173+ if ( signal !== undefined &&
174+ ( signal === null ||
175+ typeof signal !== 'object' ||
176+ ! ( 'aborted' in signal ) ) ) {
177+ return Promise . reject (
178+ new ERR_INVALID_ARG_TYPE (
179+ 'options.signal' ,
180+ 'AbortSignal' ,
181+ signal ) ) ;
182+ }
183+ // TODO(@jasnell): If a decision is made that this cannot be backported
184+ // to 12.x, then this can be converted to use optional chaining to
185+ // simplify the check.
186+ if ( signal && signal . aborted )
187+ return Promise . reject ( lazyDOMException ( 'AbortError' ) ) ;
188+ return new Promise ( ( resolve , reject ) => {
155189 const timeout = new Timeout ( resolve , after , args , false , true ) ;
156190 insert ( timeout , timeout . _idleTimeout ) ;
191+ if ( signal ) {
192+ signal . addEventListener ( 'abort' , ( ) => {
193+ clearTimeout ( timeout ) ;
194+ reject ( lazyDOMException ( 'AbortError' ) ) ;
195+ } , { once : true } ) ;
196+ }
157197 } ) ;
158198} ;
159199
@@ -272,8 +312,39 @@ function setImmediate(callback, arg1, arg2, arg3) {
272312 return new Immediate ( callback , args ) ;
273313}
274314
275- setImmediate [ customPromisify ] = function ( value ) {
276- return new Promise ( ( resolve ) => new Immediate ( resolve , [ value ] ) ) ;
315+ setImmediate [ customPromisify ] = function ( value , options = { } ) {
316+ if ( options == null || typeof options !== 'object' ) {
317+ return Promise . reject (
318+ new ERR_INVALID_ARG_TYPE (
319+ 'options' ,
320+ 'Object' ,
321+ options ) ) ;
322+ }
323+ const { signal } = options ;
324+ if ( signal !== undefined &&
325+ ( signal === null ||
326+ typeof signal !== 'object' ||
327+ ! ( 'aborted' in signal ) ) ) {
328+ return Promise . reject (
329+ new ERR_INVALID_ARG_TYPE (
330+ 'options.signal' ,
331+ 'AbortSignal' ,
332+ signal ) ) ;
333+ }
334+ // TODO(@jasnell): If a decision is made that this cannot be backported
335+ // to 12.x, then this can be converted to use optional chaining to
336+ // simplify the check.
337+ if ( signal && signal . aborted )
338+ return Promise . reject ( lazyDOMException ( 'AbortError' ) ) ;
339+ return new Promise ( ( resolve , reject ) => {
340+ const immediate = new Immediate ( resolve , [ value ] ) ;
341+ if ( signal ) {
342+ signal . addEventListener ( 'abort' , ( ) => {
343+ clearImmediate ( immediate ) ;
344+ reject ( lazyDOMException ( 'AbortError' ) ) ;
345+ } , { once : true } ) ;
346+ }
347+ } ) ;
277348} ;
278349
279350function clearImmediate ( immediate ) {
0 commit comments