• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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