@@ -28,6 +28,12 @@ const {
2828 SymbolToPrimitive
2929} = primordials ;
3030
31+ const {
32+ codes : { ERR_INVALID_ARG_TYPE }
33+ } = require ( 'internal/errors' ) ;
34+
35+ let DOMException ;
36+
3137const {
3238 immediateInfo,
3339 toggleImmediateRef
@@ -129,6 +135,11 @@ function enroll(item, msecs) {
129135 * DOM-style timers
130136 */
131137
138+ function lazyDOMException ( message ) {
139+ if ( DOMException === undefined )
140+ DOMException = internalBinding ( 'messaging' ) . DOMException ;
141+ return new DOMException ( message ) ;
142+ }
132143
133144function setTimeout ( callback , after , arg1 , arg2 , arg3 ) {
134145 validateCallback ( callback ) ;
@@ -160,11 +171,40 @@ function setTimeout(callback, after, arg1, arg2, arg3) {
160171 return timeout ;
161172}
162173
163- setTimeout [ customPromisify ] = function ( after , value ) {
174+ setTimeout [ customPromisify ] = function ( after , value , options = { } ) {
164175 const args = value !== undefined ? [ value ] : value ;
165- return new Promise ( ( resolve ) => {
176+ if ( options == null || typeof options !== 'object' ) {
177+ return Promise . reject (
178+ new ERR_INVALID_ARG_TYPE (
179+ 'options' ,
180+ 'Object' ,
181+ options ) ) ;
182+ }
183+ const { signal } = options ;
184+ if ( signal !== undefined &&
185+ ( signal === null ||
186+ typeof signal !== 'object' ||
187+ ! ( 'aborted' in signal ) ) ) {
188+ return Promise . reject (
189+ new ERR_INVALID_ARG_TYPE (
190+ 'options.signal' ,
191+ 'AbortSignal' ,
192+ signal ) ) ;
193+ }
194+ // TODO(@jasnell): If a decision is made that this cannot be backported
195+ // to 12.x, then this can be converted to use optional chaining to
196+ // simplify the check.
197+ if ( signal && signal . aborted )
198+ return Promise . reject ( lazyDOMException ( 'AbortError' ) ) ;
199+ return new Promise ( ( resolve , reject ) => {
166200 const timeout = new Timeout ( resolve , after , args , false , true ) ;
167201 insert ( timeout , timeout . _idleTimeout ) ;
202+ if ( signal ) {
203+ signal . addEventListener ( 'abort' , ( ) => {
204+ clearTimeout ( timeout ) ;
205+ reject ( lazyDOMException ( 'AbortError' ) ) ;
206+ } , { once : true } ) ;
207+ }
168208 } ) ;
169209} ;
170210
@@ -300,8 +340,39 @@ function setImmediate(callback, arg1, arg2, arg3) {
300340 return new Immediate ( callback , args ) ;
301341}
302342
303- setImmediate [ customPromisify ] = function ( value ) {
304- return new Promise ( ( resolve ) => new Immediate ( resolve , [ value ] ) ) ;
343+ setImmediate [ customPromisify ] = function ( value , options = { } ) {
344+ if ( options == null || typeof options !== 'object' ) {
345+ return Promise . reject (
346+ new ERR_INVALID_ARG_TYPE (
347+ 'options' ,
348+ 'Object' ,
349+ options ) ) ;
350+ }
351+ const { signal } = options ;
352+ if ( signal !== undefined &&
353+ ( signal === null ||
354+ typeof signal !== 'object' ||
355+ ! ( 'aborted' in signal ) ) ) {
356+ return Promise . reject (
357+ new ERR_INVALID_ARG_TYPE (
358+ 'options.signal' ,
359+ 'AbortSignal' ,
360+ signal ) ) ;
361+ }
362+ // TODO(@jasnell): If a decision is made that this cannot be backported
363+ // to 12.x, then this can be converted to use optional chaining to
364+ // simplify the check.
365+ if ( signal && signal . aborted )
366+ return Promise . reject ( lazyDOMException ( 'AbortError' ) ) ;
367+ return new Promise ( ( resolve , reject ) => {
368+ const immediate = new Immediate ( resolve , [ value ] ) ;
369+ if ( signal ) {
370+ signal . addEventListener ( 'abort' , ( ) => {
371+ clearImmediate ( immediate ) ;
372+ reject ( lazyDOMException ( 'AbortError' ) ) ;
373+ } , { once : true } ) ;
374+ }
375+ } ) ;
305376} ;
306377
307378function clearImmediate ( immediate ) {
0 commit comments