1'use strict'; 2 3const { 4 FunctionPrototypeBind, 5 Promise, 6 PromisePrototypeFinally, 7 PromiseReject, 8} = primordials; 9 10const { 11 Timeout, 12 Immediate, 13 insert 14} = require('internal/timers'); 15 16const { 17 AbortError, 18 codes: { ERR_INVALID_ARG_TYPE } 19} = require('internal/errors'); 20 21const { validateAbortSignal } = require('internal/validators'); 22 23function cancelListenerHandler(clear, reject) { 24 if (!this._destroyed) { 25 clear(this); 26 reject(new AbortError()); 27 } 28} 29 30function setTimeout(after, value, options = {}) { 31 const args = value !== undefined ? [value] : value; 32 if (options == null || typeof options !== 'object') { 33 return PromiseReject( 34 new ERR_INVALID_ARG_TYPE( 35 'options', 36 'Object', 37 options)); 38 } 39 const { signal, ref = true } = options; 40 try { 41 validateAbortSignal(signal, 'options.signal'); 42 } catch (err) { 43 return PromiseReject(err); 44 } 45 if (typeof ref !== 'boolean') { 46 return PromiseReject( 47 new ERR_INVALID_ARG_TYPE( 48 'options.ref', 49 'boolean', 50 ref)); 51 } 52 // TODO(@jasnell): If a decision is made that this cannot be backported 53 // to 12.x, then this can be converted to use optional chaining to 54 // simplify the check. 55 if (signal && signal.aborted) { 56 return PromiseReject(new AbortError()); 57 } 58 let oncancel; 59 const ret = new Promise((resolve, reject) => { 60 const timeout = new Timeout(resolve, after, args, false, true); 61 if (!ref) timeout.unref(); 62 insert(timeout, timeout._idleTimeout); 63 if (signal) { 64 oncancel = FunctionPrototypeBind(cancelListenerHandler, 65 // eslint-disable-next-line no-undef 66 timeout, clearTimeout, reject); 67 signal.addEventListener('abort', oncancel); 68 } 69 }); 70 return oncancel !== undefined ? 71 PromisePrototypeFinally( 72 ret, 73 () => signal.removeEventListener('abort', oncancel)) : ret; 74} 75 76function setImmediate(value, options = {}) { 77 if (options == null || typeof options !== 'object') { 78 return PromiseReject( 79 new ERR_INVALID_ARG_TYPE( 80 'options', 81 'Object', 82 options)); 83 } 84 const { signal, ref = true } = options; 85 try { 86 validateAbortSignal(signal, 'options.signal'); 87 } catch (err) { 88 return PromiseReject(err); 89 } 90 if (typeof ref !== 'boolean') { 91 return PromiseReject( 92 new ERR_INVALID_ARG_TYPE( 93 'options.ref', 94 'boolean', 95 ref)); 96 } 97 // TODO(@jasnell): If a decision is made that this cannot be backported 98 // to 12.x, then this can be converted to use optional chaining to 99 // simplify the check. 100 if (signal && signal.aborted) { 101 return PromiseReject(new AbortError()); 102 } 103 let oncancel; 104 const ret = new Promise((resolve, reject) => { 105 const immediate = new Immediate(resolve, [value]); 106 if (!ref) immediate.unref(); 107 if (signal) { 108 oncancel = FunctionPrototypeBind(cancelListenerHandler, 109 // eslint-disable-next-line no-undef 110 immediate, clearImmediate, reject); 111 signal.addEventListener('abort', oncancel); 112 } 113 }); 114 return oncancel !== undefined ? 115 PromisePrototypeFinally( 116 ret, 117 () => signal.removeEventListener('abort', oncancel)) : ret; 118} 119 120module.exports = { 121 setTimeout, 122 setImmediate, 123}; 124