Clear all kind of intervals - javascript

Imagine the scenario in which you have the main function which executes 2 functions which start intervals. These functions are imported as a NodeJS module and executed. Then in the main function after some time you clearIntervals. Also, note that there we'll be more intervals in the main function in the future.
So the main function is
(() => {
const intervals = [];
intervals.push(require('./simpleInterval')());
intervals.push(require('./asyncInterval')());
setTimeout(() => {
intervals.forEach(id => clearInterval(id));
}, 1200)
})();
One of these methods is simply
const intervalFoo = () => {
return setInterval(() => {
console.log('interval simple')
}, 500);
};
module.exports = intervalFoo;
but the second one contains some asynchronous code which can perform longer than interval gap but I don't want it to start before the previous "iteration" didn't finish. The solution in this kinda situation is to clear interval by id at the beginning and then reassign it at the end (but within the body) of interval. So the code of asyncInterval.js is:
const sleep = require('./utilities/sleep');
const intervalFoo = () => {
let intervalId;
const checkE2Interval = async() => {
clearInterval(intervalId);
console.log('interval async');
await sleep(120); //some long action
return intervalId = setInterval(checkE2Interval, 100);
};
return intervalId = setInterval(checkE2Interval, 100); //returning id
};
module.exports = intervalFoo;
(sleep is just a promise which resolves after timeout time given as argument)
The issue about this is that I'm returning intervalId from asyncInterval.js also in the interval and my problem is that I don't know how am I suppose to clear this thing.

Provide cancellation functions rather than providing raw handles, and pass your function an object with a flag it can check for whether it's been cancelled:
function mySetInterval(callback, ms, ...args) {
let token = {
cancelled: false
};
function wrapper(...args) {
callback(token, ...args);
if (!token.cancelled) {
id = setTimeout(wrapper, ms, ...args);
}
}
let id = setTimeout(wrapper, ms, ...args);
return function cancel() {
clearInterval(id);
token.cancelled = true;
}
}
Since 0 is an invalid timer ID, we can safely use it as a flag that the interval has been cancelled. Note that there is a slight difference between chained setTimeout (above) and setInterval (setInterval's handling of the delay between intervals is...interesting.) Also note that nothing above prevents the function being called while it's paused on sleep. To do that, you'd have to have a guard and to have the function specifically support async functions:
function mySetInterval(callback, ms, ...args) {
let token = {
cancelled: false
};
let running = false;
async function wrapper(...args) {
if (!running) {
running = true;
await callback(token, ...args);
running = false;
}
if (!token.cancelled) {
id = setTimeout(wrapper, ms, ...args);
}
}
let id = setTimeout(wrapper, ms, ...args);
return function cancel() {
clearInterval(id);
token.cancelled = true;
}
}
Use that function instead of setInterval.
In your async function, if it never has reason to stop itself:
const intervalFoo = () => {
const checkE2Interval = async(token) => {
console.log('interval async');
await sleep(120); //some long action
// If you had more logic here, you could short-circuit it by checking token.cancelled
};
return mySetInterval(checkE2Interval, 100); //returning id
};
If it does have reason to stop itself, save cancel:
const intervalFoo = () => {
let cancel = null;
const checkE2Interval = async(token) => {
console.log('interval async');
await sleep(120); //some long action
// If you had more logic here, you could short-circuit it by checking token.cancelled
// If you wanted not to continue the timer, you'd call cancel here
};
return cancel = mySetInterval(checkE2Interval, 100); //returning id
};
Then, where you need to cancel:
(() => {
const cancellers = [];
cancellers.push(require('./simpleInterval')());
cancellers.push(require('./asyncInterval')());
setTimeout(() => {
cancellers.forEach(cancel => cancel());
}, 1200)
})();
Live Example:
const sleep = ms => new Promise(resolve => {
setTimeout(resolve, ms);
});
function mySetInterval(callback, ms, ...args) {
let token = {
cancelled: false
};
function wrapper(...args) {
callback(token, ...args);
if (!token.cancelled) {
id = setTimeout(wrapper, ms, ...args);
}
}
let id = setTimeout(wrapper, ms, ...args);
return function cancel() {
clearInterval(id);
token.cancelled = true;
}
}
const intervalFoo = () => {
let cancel = null;
const checkE2Interval = async(token) => {
console.log('interval async');
await sleep(120); //some long action
// If you had more logic here, you could short-circuit it by checking token.cancelled
// If you wanted not to continue the timer, you'd call cancel here
};
return cancel = mySetInterval(checkE2Interval, 100); //returning id
};
(() => {
const cancellers = [];
cancellers.push(intervalFoo());
setTimeout(() => {
console.log("Cancelling");
cancellers.forEach(cancel => {
cancel();
});
}, 1200)
})();
Live Example with the running flag:
const sleep = ms => new Promise(resolve => {
setTimeout(resolve, ms);
});
function mySetInterval(callback, ms, ...args) {
let token = {
cancelled: false
};
let running = false;
async function wrapper(...args) {
if (!running) {
running = true;
await callback(token, ...args);
running = false;
}
if (!token.cancelled) {
id = setTimeout(wrapper, ms, ...args);
}
}
let id = setTimeout(wrapper, ms, ...args);
return function cancel() {
clearInterval(id);
token.cancelled = true;
}
}
const intervalFoo = () => {
let cancel = null;
const checkE2Interval = async(token) => {
console.log('interval async');
await sleep(120); //some long action
console.log('awake');
// If you had more logic here, you could short-circuit it by checking token.cancelled
// If you wanted not to continue the timer, you'd call cancel here
};
return cancel = mySetInterval(checkE2Interval, 100); //returning id
};
(() => {
const cancellers = [];
cancellers.push(intervalFoo());
setTimeout(() => {
console.log("Cancelling");
cancellers.forEach(cancel => {
cancel();
});
}, 1200)
})();
You can generalize this further, but you get the basic idea.

Related

How i can call function inside function

I would like to call a function that is inside another function, that function will clear the timeout.
I have tried the following code, but without success:
async function Blast2() {
const delayTime = 1000;
const timer = (ms) => new Promise((res) => setTimeout(res, ms));
function ClearDelayTime() {
return clearTimeout(blast);
}
const blast = setTimeout(function () {
let blast =
"SELECT * FROM admin_contacts,temporary WHERE blast_status = 'sended'";
db.query(blast, async function (err, result, field) {
if (err) throw err;
loop: {
for (var i = 0; i < result.length; i++) {
console.log(result[i].telefone);
await timer(delayTime); // then the created Promise can be awaited
}
}
});
}, delayTime);
}
// I Want Call the function ClearDelayTime() inside Blast2()
Blast2().ClearDelayTime();
I've refactored your code. It now immediately returns a function that can be used to abort.
const db = {
query: (sql, callback) => callback(undefined,
[{telefone:1},{telefone:2},{telefone:3},{telefone:4},{telefone:5}])
}
function blast2() {
const delayTime = 1000
const timer = ms => new Promise(res => setTimeout(res, ms))
let sql = "SELECT * FROM admin_contacts,temporary WHERE blast_status = 'sended'";
let abort = false;
db.query(sql, (err, result) => {
if (!err) {
(async () => {
for(let i=0; i<result.length && !abort; i++) {
console.log(result[i].telefone);
await timer(delayTime);
}
})();
}
})
return () => abort = true;
}
let abortFunc = blast2();
setTimeout(abortFunc, 2500);
Your function Blast2 doesn't return anything. If you want to call a function inside it, you could return and store it using a variable, then call it.
Something like this:
const f1 = () => {
console.log('f1 called')
const f2 = () => {
console.log('f2 called')
}
return f2
}
const returnedFunction = f1()
console.log(returnedFunction())

Canceling setTimeout early

I'm working on an audio recording class that either runs for an allotted period of time (such as 5 seconds) or can be stopped early by the user.
I'm using setTimeout to define the recording length, which works. However, I'm having trouble getting setTimeout working with a "stop" button. The error is as follows:
Cannot read properties of null (reading 'stop')
When the startRecording function executes, the handleStopRecording function is called which sets a timer with the "stopRecording" function. If the "stopRecording" function is called before the time elapses (by pressing the "stop" button), the function call that was initially in setTimeout will still execute when the timer expires, causing an error.
I tried fixing this by using clearTimeout, but then the "context" of the original function call is lost and we get the same error:
Cannot read properties of null (reading 'stop')
Unless I'm mistaken, I think this is an issue with closure of the setTimeout function - however I'm not sure how to clear the function early with a stop button and limit recording time.
Thank you in advance!
App.js (React.js)
import AudioRecorder from "./audioRecorder";
const App = () => {
const [recordedNameClipURL, setRecordedNameClipURL] = useState(null);
const [timeoutId, setTimeoutId] = useState(null);
const recorder = new AudioRecorder();
const startRecording = () => {
recorder.start();
handleStopRecording();
};
const handleStopRecording = async () => {
const id = setTimeout(stopRecording, 3000);
setTimeoutId(id);
};
const stopRecording = async () => {
clearTimeout(timeoutId);
const response = await recorder.stop();
setRecordedNameClipURL(response);
};
return (
...
);
};
audioRecorder.js
class AudioRecorder {
constructor() {
this.audioRecorder = null;
this.audioChunks = [];
}
initialize = async () => {
try {
await this.isSupported();
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.audioRecorder = new MediaRecorder(stream);
this.audioRecorder.addEventListener("dataavailable", event => {
this.audioChunks.push(event.data);
});
} catch (err) {
console.log(err.message);
}
};
start = async () => {
try {
await this.initialize();
this.audioRecorder.start();
} catch (err) {
console.log(err.message);
}
};
stop = async () => {
try {
this.audioRecorder.stop();
const blob = await this.stopStream();
return URL.createObjectURL(blob);
} catch (err) {
console.log(err.message);
}
};
stopStream = () => {
return new Promise(resolve => {
this.audioRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(this.audioChunks, {
type: this.audioRecorder.mimeType,
});
resolve(audioBlob);
});
});
};
isSupported = () => {
return new Promise((resolve, reject) => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
resolve(true);
}
reject(new Error("getUserMedia not supported on this browser!"));
});
};
}
export default AudioRecorder;
Store the timer inside a React Ref instead
I usually store timeout/interval IDs in a React ref, because storing the handle isn't really "application state" the way that other things are. Sometimes it's needed to avoid render thrashing.
Here's what that looks like:
let timerRef = React.useRef(null)
const handleStopRecording = async () => {
timerRef.current = setTimeout(stopRecording, 3000)
}
const stopRecording = async () => {
clearTimeout(timerRef.current)
timerRef.current = null // good idea to clean up your data
const response = await recorder.stop()
setRecordedNameClipURL(response)
}
Code that needs to know if the timer is running should consult the timerRef; no additional state is needed:
let timerIsRunning = !!timerRef.current
You can try using a boolean value to check if the process is stopped. You can store it in state and change its value when starting or stopping
const [isStopped, setIsStopped] = useState(false);
const handleStopRecording = async () => {
const id = setTimeout(() => {
if(!isStopped){
stopRecording
}
}, 3000);
setTimeoutId(id);
};

Why all methods in chain executed at the same time?

Why all methods in chain executed at the same time? I wrap method in setTimeout. What I did wrong?
I know how to do this with promises but I'm learning chaining technique.
const delay = function delay(func) {
return function(...args) {
setTimeout(() => {
return func.apply(this, [...args]);
}, 2000);
return this;
};
}
class TimePopup {
constructor() {
this.firstMethod = delay(this.firstMethod);
this.secondMethod = delay(this.secondMethod);
this.thirdMethod = delay(this.thirdMethod);
console.log('This is the constructor');
}
firstMethod(val) {
console.log('This is the First Method', val);
}
secondMethod(val) {
console.log('This is the Second Method', val);
}
thirdMethod(val) {
console.log('This is the Third Method', val);
}
}
new TimePopup()
.firstMethod(1)
.secondMethod(2)
.thirdMethod(3);
The issue you have is that delay does not schedule things to run relative to each other. Each thing is delayed exactly the time it's called, so new TimePopup().firstMethod(1).secondMethod(2).thirdMethod(3); will call all three methods at the same time and each is delayed the same amount of time relative to the call time. Then all three fire after two seconds.
Instead, you need to process them sequentially. Similar to this answer of mine, you can use create a promise queue to handle the sequence. Each method adds to the chain instead and there is a delay between before it's executed:
const wait = ms => () =>
new Promise(resolve => setTimeout(resolve, ms));
let queue = Promise.resolve();
function delay(func) {
return function(...args) {
queue = queue
.then(wait(2000))
.then(() => func.apply(this, [...args]));
return this;
};
}
class TimePopup {
constructor() {
this.firstMethod = delay(this.firstMethod);
this.secondMethod = delay(this.secondMethod);
this.thirdMethod = delay(this.thirdMethod);
console.log('This is the constructor');
}
firstMethod(val) {
console.log('This is the First Method', val);
}
secondMethod(val) {
console.log('This is the Second Method', val);
}
thirdMethod(val) {
console.log('This is the Third Method', val);
}
}
new TimePopup()
.firstMethod(1)
.secondMethod(2)
.thirdMethod(3);
A slightly more reusable approach is to create the delay functions on demand, so you can have different queues for different tasks. This can look like this:
const delayMaker = delayBetween => {
let queue = Promise.resolve();
return function delay(func) {
return function(...args) {
queue = queue
.then(wait(delayBetween))
.then(() => func.apply(this, [...args]));
return this;
};
}
}
/* ... */
const delay = delayMaker(2000);
const wait = ms => () =>
new Promise(resolve => setTimeout(resolve, ms));
const delayMaker = delayBetween => {
let queue = Promise.resolve();
return function delay(func) {
return function(...args) {
queue = queue
.then(wait(delayBetween))
.then(() => func.apply(this, [...args]));
return this;
};
}
}
class TimePopup {
constructor() {
const delay = delayMaker(2000);
this.firstMethod = delay(this.firstMethod);
this.secondMethod = delay(this.secondMethod);
this.thirdMethod = delay(this.thirdMethod);
console.log('This is the constructor');
}
firstMethod(val) {
console.log('This is the First Method', val);
}
secondMethod(val) {
console.log('This is the Second Method', val);
}
thirdMethod(val) {
console.log('This is the Third Method', val);
}
}
new TimePopup()
.firstMethod(1)
.secondMethod(2)
.thirdMethod(3);
In the pre-promise days one popular approach was to pass around a next function which is supposed to shift the next queued task from the stack and run it. A simplified example:
class App {
funcs = [];
use(func) {
this.funcs.push(func)
return this
}
next() {
if (this.funcs.length)
this.funcs.shift()(this.next.bind(this))
}
}
//
function delay(func) {
return function (next) {
setTimeout(() => {
func();
next();
}, 1000);
}
}
//
(new App())
.use(delay(() => console.log(1)))
.use(delay(() => console.log(2)))
.use(delay(() => console.log(3)))
.next()

Is async code in a Promise always an antipattern?

I see from this question that it can be an antipattern to mix Promises with async code.
Does this, however, apply in all cases?
I can't see an easy way to avoid combining them in the following code:
It's an alternative to setInterval which waits for an invocation to complete before scheduling the next one
However, unlike recursive setTimeout, it does not constitute an open memory leak in browsers that don't yet support TCO
Does this code embody an antipattern? And, if so, how can I remedy it without introducing a memory leak?
See, in particular, line 10: new Promise( async (resolve) => {
—this seems very non-idiomatic, but I don't see another way to accomplish: wrapping an await statement in a while loop per se, dispatching it, and returning a handle to abort the loop.
var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const repeatedTimeoutIntervals = [];
function setRepeatedTimeout(f, delay, ...arguments) {
//Like setInterval, but waits for an invocation to complete before scheduling the next one
//(Supports both classic and async functions)
const mySemaphores = {notAborted: true};
const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
new Promise( async (resolve) => {
await asleep(delay);
while(mySemaphores.notAborted) {
await f(...arguments);
await asleep(delay);
}
delete repeatedTimeoutIntervals[intervalID];
});
return intervalID;
}
function clearRepeatedTimeout(intervalID) {
//Clears loops set by setInterval()
repeatedTimeoutIntervals[intervalID].notAborted = false;
}
return [setRepeatedTimeout, clearRepeatedTimeout];
})();
<p><button onclick="(function createInterval(){
const _ = {intervalID: undefined};
_.intervalID = setRepeatedTimeout( () => {
console.log(`Hello from intervalID ${_.intervalID}`)
}, 2000)
})()">Create timer</button><br />
<form action="javascript:void(0);" onsubmit="(function clearInterval(intervalID){
clearRepeatedTimeout(intervalID);
})(parseInt(event.target.elements.intervalID.value))">
<input name="intervalID" placeholder="intervalID"/><button type="submit">Clear timer</button></p>
The problem that the other question was warning about, and that could be a problem here, is that if the inside of the async callback passed to the Promise constructor awaits something that rejects, the Promise will hang instead of rejecting. Your current code will not result in f ever rejecting, but setRepeatedTimeout were to carry out a task which may reject, you'd get an unhandled rejection and permanent hanging:
var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const repeatedTimeoutIntervals = [];
function setRepeatedTimeout(f, delay, ...arguments) {
//Like setInterval, but waits for an invocation to complete before scheduling the next one
//(Supports both classic and async functions)
const mySemaphores = {notAborted: true};
const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
new Promise( async (resolve) => {
await asleep(delay);
while(mySemaphores.notAborted) {
await f(...arguments);
await asleep(delay);
}
delete repeatedTimeoutIntervals[intervalID];
});
return intervalID;
}
function clearRepeatedTimeout(intervalID) {
//Clears loops set by setInterval()
repeatedTimeoutIntervals[intervalID].notAborted = false;
}
return [setRepeatedTimeout, clearRepeatedTimeout];
})();
const _ = { intervalID: undefined };
_.intervalID = setRepeatedTimeout(() => {
console.log('Throwing...');
return Promise.reject();
}, 2000)
If you want the loop to continue when such an error is encountered, there's a way to handle such problems while keeping the async: just catch everything that might reject (either in a try/catch or with .catch):
var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const repeatedTimeoutIntervals = [];
function setRepeatedTimeout(f, delay, ...arguments) {
//Like setInterval, but waits for an invocation to complete before scheduling the next one
//(Supports both classic and async functions)
const mySemaphores = {notAborted: true};
const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
new Promise( async (resolve) => {
await asleep(delay);
while(mySemaphores.notAborted) {
await f(...arguments).catch(() => {}); // log error here if you want
await asleep(delay);
}
delete repeatedTimeoutIntervals[intervalID];
});
return intervalID;
}
function clearRepeatedTimeout(intervalID) {
//Clears loops set by setInterval()
repeatedTimeoutIntervals[intervalID].notAborted = false;
}
return [setRepeatedTimeout, clearRepeatedTimeout];
})();
const _ = { intervalID: undefined };
_.intervalID = setRepeatedTimeout(() => {
console.log('Throwing...');
return Promise.reject();
}, 2000)
But there's really no need for the new Promise here at all - it never resolves, and never gets used. Just use an async IIFE:
var [setRepeatedTimeout, clearRepeatedTimeout] = (() => {
const asleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const repeatedTimeoutIntervals = [];
function setRepeatedTimeout(f, delay, ...arguments) {
//Like setInterval, but waits for an invocation to complete before scheduling the next one
//(Supports both classic and async functions)
const mySemaphores = {notAborted: true};
const intervalID = repeatedTimeoutIntervals.push(mySemaphores) - 1;
(async () => {
await asleep(delay);
while(mySemaphores.notAborted) {
await f(...arguments).catch(() => {}); // log error here if you want
await asleep(delay);
}
delete repeatedTimeoutIntervals[intervalID];
})();
return intervalID;
}
function clearRepeatedTimeout(intervalID) {
//Clears loops set by setInterval()
repeatedTimeoutIntervals[intervalID].notAborted = false;
}
return [setRepeatedTimeout, clearRepeatedTimeout];
})();
const _ = { intervalID: undefined };
_.intervalID = setRepeatedTimeout(() => {
console.log('Throwing...');
return Promise.reject();
}, 2000)
(async () => {
await asleep(delay);
while(mySemaphores.notAborted) {
await f(...arguments).catch(() => {}); // log error here if you want
await asleep(delay);
}
delete repeatedTimeoutIntervals[intervalID];
})();

How to build an event generator in JavaScript

I am trying to build a way to create a generator which can yield DOM events. More generally, I want to create a way to convert an event system to an async system yielding events.
My initial code example works, but I can see an issue with lifting the resolve function from the Promise so that I can call that function once the event comes in.
class EventPropagation {
constructor(id) {
const button = document.getElementById(id);
let _resolve;
button.addEventListener("click", event => {
if (_resolve) {
_resolve(event);
}
});
let _listen = () => {
return new Promise(resolve => {
_resolve = resolve;
});
}
this.subscribe = async function*() {
const result = await _listen();
yield result;
yield * this.subscribe();
}
}
}
async function example() {
const eventPropagation = new EventPropagation("btn");
for await (const event of eventPropagation.subscribe()) {
console.log(event);
}
}
// call the example function
example();
My question is: Is there a better way of building something like this? There are a lot of things to think about, like multiple events coming in at the same time or cleaning up the listener and the subscriptions. My goal is not to end up with a reactive library but I do want to create small transparent functions which yield events asynchronously.
fiddle
Edited 14 dec 2017 (Edited in response to Bergi's comment)
Async Generators
Babel and a few plugins later; async generators aren't a problem:
const throttle = ms => new Promise(resolve => setTimeout(resolve, ms));
const getData = async() => {
const randomValue = Math.floor(Math.random() * 5000 + 1);
await throttle(randomValue);
return `The random value was: ${randomValue}`;
}
async function* asyncRandomMessage() {
const message = await getData();
yield message;
// recursive call
yield *asyncRandomMessage();
}
async function example() {
for await (const message of asyncRandomMessage()) {
console.log(message);
}
}
// call it at your own risk, it does not stop
// example();
What I want to know is how I transform a series of individual callback calls into an async stream. I can't imagine this problem isn't tackled. When I look at the library Bergi showed in the comments I see the same implementation as I did, namely: "Store the resolve and reject functions somewhere the event handler can call them." I can't imagine that would be a correct way of solving this problem.
You need a event bucket, here is an example:
function evtBucket() {
const stack = [],
iterate = bucket();
var next;
async function * bucket() {
while (true) {
yield new Promise((res) => {
if (stack.length > 0) {
return res(stack.shift());
}
next = res;
});
}
}
iterate.push = (itm) => {
if (next) {
next(itm);
next = false;
return;
}
stack.push(itm);
}
return iterate;
}
;(async function() {
let evts = evtBucket();
setInterval(()=>{
evts.push(Date.now());
evts.push(Date.now() + '++');
}, 1000);
for await (let evt of evts) {
console.log(evt);
}
})();
My best solution thus far has been to have an internal EventTarget that dispatches events when new events are added onto a queue array. This is what I've been working on for a JS modules library (including used modules here). I don't like it... But it works.
Note: This also handles the new AbortSignal option for event listeners in multiple places.
export function isAborted(signal) {
if (signal instanceof AbortController) {
return signal.signal.aborted;
} else if (signal instanceof AbortSignal) {
return signal.aborted;
} else {
return false;
}
}
export async function when(target, event, { signal } = {}) {
await new Promise(resolve => {
target.addEventListener(event, resolve, { once: true, signal });
});
}
export async function *yieldEvents(what, event, { capture, passive, signal } = {}) {
const queue = [];
const target = new EventTarget();
what.addEventListener(event, event => {
queue.push(event);
target.dispatchEvent(new Event('enqueued'));
}, { capture, passive, signal });
while (! isAborted(signal)) {
if (queue.length === 0) {
await when(target, 'enqueued', { signal }).catch(e => {});
}
/**
* May have aborted between beginning of loop and now
*/
if (isAborted(signal)) {
break;
} else {
yield queue.shift();
}
}
}
The example provided by NSD, but now in Typescript
class AsyncQueue<T> {
private queue: T[] = [];
private maxQueueLength = Infinity;
private nextResolve = (value: T) => {};
private hasNext = false;
constructor(maxQueueLength?: number) {
if (maxQueueLength) {
this.maxQueueLength = maxQueueLength;
}
}
async *[Symbol.asyncIterator]() {
while (true) {
yield new Promise((resolve) => {
if (this.queue.length > 0) {
return resolve(this.queue.shift());
}
this.nextResolve = resolve;
this.hasNext = true;
});
}
}
push(item: T) {
if (this.hasNext) {
this.nextResolve(item);
this.hasNext = false;
return;
}
if (this.queue.length > this.maxQueueLength) {
this.queue.shift();
}
this.queue.push(item);
}
}
(async function () {
const queueu = new AsyncQueue<string>();
setInterval(() => {
queueu.push(Date.now().toString());
queueu.push(Date.now().toString() + "++");
}, 1000);
for await (const evt of queueu) {
console.log(evt);
}
})();

Categories