• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --no-warnings --expose-internals
2'use strict';
3const common = require('../common');
4const assert = require('assert');
5const timers = require('timers');
6const { promisify } = require('util');
7const child_process = require('child_process');
8
9const { getEventListeners } = require('events');
10const { NodeEventTarget } = require('internal/event_target');
11
12const timerPromises = require('timers/promises');
13
14const setPromiseTimeout = promisify(timers.setTimeout);
15const exec = promisify(child_process.exec);
16
17const { setInterval } = timerPromises;
18
19process.on('multipleResolves', common.mustNotCall());
20
21{
22  const iterable = setInterval(1, undefined);
23  const iterator = iterable[Symbol.asyncIterator]();
24  const promise = iterator.next();
25  promise.then(common.mustCall((result) => {
26    assert.ok(!result.done, 'iterator was wrongly marked as done');
27    assert.strictEqual(result.value, undefined);
28    return iterator.return();
29  })).then(common.mustCall());
30}
31
32{
33  const iterable = setInterval(1, 'foobar');
34  const iterator = iterable[Symbol.asyncIterator]();
35  const promise = iterator.next();
36  promise.then(common.mustCall((result) => {
37    assert.ok(!result.done, 'iterator was wronly marked as done');
38    assert.strictEqual(result.value, 'foobar');
39    return iterator.return();
40  })).then(common.mustCall());
41}
42
43{
44  const iterable = setInterval(1, 'foobar');
45  const iterator = iterable[Symbol.asyncIterator]();
46  const promise = iterator.next();
47  promise
48    .then(common.mustCall((result) => {
49      assert.ok(!result.done, 'iterator was wronly marked as done');
50      assert.strictEqual(result.value, 'foobar');
51      return iterator.next();
52    }))
53    .then(common.mustCall((result) => {
54      assert.ok(!result.done, 'iterator was wrongly marked as done');
55      assert.strictEqual(result.value, 'foobar');
56      return iterator.return();
57    }))
58    .then(common.mustCall());
59}
60
61{
62  const signal = AbortSignal.abort(); // Abort in advance
63
64  const iterable = setInterval(1, undefined, { signal });
65  const iterator = iterable[Symbol.asyncIterator]();
66  assert.rejects(iterator.next(), /AbortError/).then(common.mustCall());
67}
68
69{
70  const ac = new AbortController();
71  const { signal } = ac;
72
73  const iterable = setInterval(100, undefined, { signal });
74  const iterator = iterable[Symbol.asyncIterator]();
75
76  // This promise should take 100 seconds to resolve, so now aborting it should
77  // mean we abort early
78  const promise = iterator.next();
79
80  ac.abort(); // Abort in after we have a next promise
81
82  assert.rejects(promise, /AbortError/).then(common.mustCall());
83}
84
85{
86  // Check aborting after getting a value.
87  const ac = new AbortController();
88  const { signal } = ac;
89
90  const iterable = setInterval(100, undefined, { signal });
91  const iterator = iterable[Symbol.asyncIterator]();
92
93  const promise = iterator.next();
94  const abortPromise = promise.then(common.mustCall(() => ac.abort()))
95    .then(() => iterator.next());
96  assert.rejects(abortPromise, /AbortError/).then(common.mustCall());
97}
98
99{
100  [1, '', Infinity, null, {}].forEach((ref) => {
101    const iterable = setInterval(10, undefined, { ref });
102    assert.rejects(() => iterable[Symbol.asyncIterator]().next(), /ERR_INVALID_ARG_TYPE/)
103      .then(common.mustCall());
104  });
105
106  [1, '', Infinity, null, {}].forEach((signal) => {
107    const iterable = setInterval(10, undefined, { signal });
108    assert.rejects(() => iterable[Symbol.asyncIterator]().next(), /ERR_INVALID_ARG_TYPE/)
109      .then(common.mustCall());
110  });
111
112  [1, '', Infinity, null, true, false].forEach((options) => {
113    const iterable = setInterval(10, undefined, options);
114    assert.rejects(() => iterable[Symbol.asyncIterator]().next(), /ERR_INVALID_ARG_TYPE/)
115      .then(common.mustCall());
116  });
117}
118
119{
120  // Check that timer adding signals does not leak handlers
121  const signal = new NodeEventTarget();
122  signal.aborted = false;
123  const iterator = setInterval(1, undefined, { signal });
124  iterator.next().then(common.mustCall(() => {
125    assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
126    iterator.return();
127  })).finally(common.mustCall(() => {
128    assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
129  }));
130}
131
132{
133  // Check that break removes the signal listener
134  const signal = new NodeEventTarget();
135  signal.aborted = false;
136  async function tryBreak() {
137    const iterator = setInterval(10, undefined, { signal });
138    let i = 0;
139    // eslint-disable-next-line no-unused-vars
140    for await (const _ of iterator) {
141      if (i === 0) {
142        assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
143      }
144      i++;
145      if (i === 2) {
146        break;
147      }
148    }
149    assert.strictEqual(i, 2);
150    assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
151  }
152
153  tryBreak().then(common.mustCall());
154}
155
156{
157  exec(`${process.execPath} -pe "const assert = require('assert');` +
158    'const interval = require(\'timers/promises\')' +
159    '.setInterval(1000, null, { ref: false });' +
160    'interval[Symbol.asyncIterator]().next()' +
161    '.then(assert.fail)"').then(common.mustCall(({ stderr }) => {
162    assert.strictEqual(stderr, '');
163  }));
164}
165
166{
167  async function runInterval(fn, intervalTime, signal) {
168    const input = 'foobar';
169    const interval = setInterval(intervalTime, input, { signal });
170    let iteration = 0;
171    for await (const value of interval) {
172      assert.strictEqual(value, input);
173      iteration++;
174      await fn(iteration);
175    }
176  }
177
178  {
179    // Check that we call the correct amount of times.
180    const controller = new AbortController();
181    const { signal } = controller;
182
183    let loopCount = 0;
184    const delay = 20;
185    const timeoutLoop = runInterval(() => {
186      loopCount++;
187      if (loopCount === 5) controller.abort();
188      if (loopCount > 5) throw new Error('ran too many times');
189    }, delay, signal);
190
191    assert.rejects(timeoutLoop, /AbortError/).then(common.mustCall(() => {
192      assert.strictEqual(loopCount, 5);
193    }));
194  }
195
196  {
197    // Check that if we abort when we have some unresolved callbacks,
198    // we actually call them.
199    const controller = new AbortController();
200    const { signal } = controller;
201    const delay = 10;
202    let totalIterations = 0;
203    const timeoutLoop = runInterval(async (iterationNumber) => {
204      await setPromiseTimeout(delay * 4);
205      if (iterationNumber <= 2) {
206        assert.strictEqual(signal.aborted, false);
207      }
208      if (iterationNumber === 2) {
209        controller.abort();
210      }
211      if (iterationNumber > 2) {
212        assert.strictEqual(signal.aborted, true);
213      }
214      if (iterationNumber > totalIterations) {
215        totalIterations = iterationNumber;
216      }
217    }, delay, signal);
218
219    timeoutLoop.catch(common.mustCall(() => {
220      assert.ok(totalIterations >= 3, `iterations was ${totalIterations} < 3`);
221    }));
222  }
223}
224
225{
226  // Check that the timing is correct
227  let pre = false;
228  let post = false;
229
230  const time_unit = 50;
231  Promise.all([
232    setPromiseTimeout(1).then(() => pre = true),
233    new Promise((res) => {
234      const iterable = timerPromises.setInterval(time_unit * 2);
235      const iterator = iterable[Symbol.asyncIterator]();
236
237      iterator.next().then(() => {
238        assert.ok(pre, 'interval ran too early');
239        assert.ok(!post, 'interval ran too late');
240        return iterator.next();
241      }).then(() => {
242        assert.ok(post, 'second interval ran too early');
243        return iterator.return();
244      }).then(res);
245    }),
246    setPromiseTimeout(time_unit * 3).then(() => post = true),
247  ]).then(common.mustCall());
248}
249
250(async () => {
251  const signal = AbortSignal.abort('boom');
252  try {
253    const iterable = timerPromises.setInterval(2, undefined, { signal });
254    // eslint-disable-next-line no-unused-vars, no-empty
255    for await (const _ of iterable) { }
256    assert.fail('should have failed');
257  } catch (err) {
258    assert.strictEqual(err.cause, 'boom');
259  }
260})().then(common.mustCall());
261