2020-06-18 22:22:17 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const {
|
|
|
|
Promise,
|
2020-11-06 16:07:43 +01:00
|
|
|
PromisePrototypeFinally,
|
2020-06-18 22:22:17 +02:00
|
|
|
PromiseReject,
|
|
|
|
} = primordials;
|
|
|
|
|
|
|
|
const {
|
|
|
|
Timeout,
|
|
|
|
Immediate,
|
|
|
|
insert
|
|
|
|
} = require('internal/timers');
|
|
|
|
|
|
|
|
const {
|
|
|
|
hideStackFrames,
|
|
|
|
codes: { ERR_INVALID_ARG_TYPE }
|
|
|
|
} = require('internal/errors');
|
|
|
|
|
|
|
|
let DOMException;
|
|
|
|
|
2020-08-13 21:00:55 +02:00
|
|
|
const lazyDOMException = hideStackFrames((message, name) => {
|
2020-06-18 22:22:17 +02:00
|
|
|
if (DOMException === undefined)
|
|
|
|
DOMException = internalBinding('messaging').DOMException;
|
2020-08-13 21:00:55 +02:00
|
|
|
return new DOMException(message, name);
|
2020-06-18 22:22:17 +02:00
|
|
|
});
|
|
|
|
|
2020-11-06 16:07:43 +01:00
|
|
|
function cancelListenerHandler(clear, reject) {
|
|
|
|
if (!this._destroyed) {
|
|
|
|
clear(this);
|
|
|
|
reject(lazyDOMException('The operation was aborted', 'AbortError'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 22:22:17 +02:00
|
|
|
function setTimeout(after, value, options = {}) {
|
|
|
|
const args = value !== undefined ? [value] : value;
|
|
|
|
if (options == null || typeof options !== 'object') {
|
|
|
|
return PromiseReject(
|
|
|
|
new ERR_INVALID_ARG_TYPE(
|
|
|
|
'options',
|
|
|
|
'Object',
|
|
|
|
options));
|
|
|
|
}
|
|
|
|
const { signal, ref = true } = options;
|
|
|
|
if (signal !== undefined &&
|
|
|
|
(signal === null ||
|
|
|
|
typeof signal !== 'object' ||
|
|
|
|
!('aborted' in signal))) {
|
|
|
|
return PromiseReject(
|
|
|
|
new ERR_INVALID_ARG_TYPE(
|
|
|
|
'options.signal',
|
|
|
|
'AbortSignal',
|
|
|
|
signal));
|
|
|
|
}
|
|
|
|
if (typeof ref !== 'boolean') {
|
|
|
|
return PromiseReject(
|
|
|
|
new ERR_INVALID_ARG_TYPE(
|
|
|
|
'options.ref',
|
|
|
|
'boolean',
|
|
|
|
ref));
|
|
|
|
}
|
|
|
|
// TODO(@jasnell): If a decision is made that this cannot be backported
|
|
|
|
// to 12.x, then this can be converted to use optional chaining to
|
|
|
|
// simplify the check.
|
2020-08-13 21:00:55 +02:00
|
|
|
if (signal && signal.aborted) {
|
|
|
|
return PromiseReject(
|
|
|
|
lazyDOMException('The operation was aborted', 'AbortError'));
|
|
|
|
}
|
2020-11-06 16:07:43 +01:00
|
|
|
let oncancel;
|
|
|
|
const ret = new Promise((resolve, reject) => {
|
2020-06-18 22:22:17 +02:00
|
|
|
const timeout = new Timeout(resolve, after, args, false, true);
|
|
|
|
if (!ref) timeout.unref();
|
|
|
|
insert(timeout, timeout._idleTimeout);
|
|
|
|
if (signal) {
|
2020-11-06 16:07:43 +01:00
|
|
|
// eslint-disable-next-line no-undef
|
|
|
|
oncancel = cancelListenerHandler.bind(timeout, clearTimeout, reject);
|
|
|
|
signal.addEventListener('abort', oncancel);
|
2020-06-18 22:22:17 +02:00
|
|
|
}
|
|
|
|
});
|
2020-11-06 16:07:43 +01:00
|
|
|
return oncancel !== undefined ?
|
|
|
|
PromisePrototypeFinally(
|
|
|
|
ret,
|
|
|
|
() => signal.removeEventListener('abort', oncancel)) : ret;
|
2020-06-18 22:22:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function setImmediate(value, options = {}) {
|
|
|
|
if (options == null || typeof options !== 'object') {
|
|
|
|
return PromiseReject(
|
|
|
|
new ERR_INVALID_ARG_TYPE(
|
|
|
|
'options',
|
|
|
|
'Object',
|
|
|
|
options));
|
|
|
|
}
|
|
|
|
const { signal, ref = true } = options;
|
|
|
|
if (signal !== undefined &&
|
|
|
|
(signal === null ||
|
|
|
|
typeof signal !== 'object' ||
|
|
|
|
!('aborted' in signal))) {
|
|
|
|
return PromiseReject(
|
|
|
|
new ERR_INVALID_ARG_TYPE(
|
|
|
|
'options.signal',
|
|
|
|
'AbortSignal',
|
|
|
|
signal));
|
|
|
|
}
|
|
|
|
if (typeof ref !== 'boolean') {
|
|
|
|
return PromiseReject(
|
|
|
|
new ERR_INVALID_ARG_TYPE(
|
|
|
|
'options.ref',
|
|
|
|
'boolean',
|
|
|
|
ref));
|
|
|
|
}
|
|
|
|
// TODO(@jasnell): If a decision is made that this cannot be backported
|
|
|
|
// to 12.x, then this can be converted to use optional chaining to
|
|
|
|
// simplify the check.
|
2020-08-13 21:00:55 +02:00
|
|
|
if (signal && signal.aborted) {
|
|
|
|
return PromiseReject(
|
|
|
|
lazyDOMException('The operation was aborted', 'AbortError'));
|
|
|
|
}
|
2020-11-06 16:07:43 +01:00
|
|
|
let oncancel;
|
|
|
|
const ret = new Promise((resolve, reject) => {
|
2020-06-18 22:22:17 +02:00
|
|
|
const immediate = new Immediate(resolve, [value]);
|
|
|
|
if (!ref) immediate.unref();
|
|
|
|
if (signal) {
|
2020-11-06 16:07:43 +01:00
|
|
|
// eslint-disable-next-line no-undef
|
|
|
|
oncancel = cancelListenerHandler.bind(immediate, clearImmediate, reject);
|
|
|
|
signal.addEventListener('abort', oncancel);
|
2020-06-18 22:22:17 +02:00
|
|
|
}
|
|
|
|
});
|
2020-11-06 16:07:43 +01:00
|
|
|
return oncancel !== undefined ?
|
|
|
|
PromisePrototypeFinally(
|
|
|
|
ret,
|
|
|
|
() => signal.removeEventListener('abort', oncancel)) : ret;
|
2020-06-18 22:22:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
setTimeout,
|
|
|
|
setImmediate,
|
|
|
|
};
|