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