-
Notifications
You must be signed in to change notification settings - Fork 0
/
timer.js
100 lines (94 loc) · 2.99 KB
/
timer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
(function(channelNameSize){
class Queue{
//each node is in form [data,next]
top=null;
end=null;
constructor(){}
add(data){
const node=[data,null];
this.top? (this.end[1]=node,this.end=node): (this.top=node,this.end=node);
}
pop(){
if(!this.top) return null;
const [data,next]=this.top;
return (this.top=next,data);
}
empty(){return !this.top}
}
const timers={__proto__:null,length:0,count:0,queue:new Queue()};
const crypto=typeof window==="undefined"?require('node:crypto'):window.crypto;
const mapping=[], typedarray=new Uint8Array(channelNameSize);
for(let i=0;i<256;i++) mapping[i]=String.fromCharCode(i);
function randomChannel(input){
if(input){
const num=timers.queue.empty()? timers.length++: timers.queue.pop();
return (timers[num]=input),timers.count++,num;
}
//below is support in same function but for cryptographically secure random channel name
var arr=crypto.getRandomValues(typedarray), str="";
for(let i=0;i<arr.length;i++) str+=mapping[arr[i]];
return str;
}
//timer engine begin
let channel=randomChannel(), sender=null, receiver=null;
function listener(){
if(!timers.count) return deactivate();
sender.postMessage(null); //repeat the channel messaging IF TIMER(S) EXIST
//the idea here is something that must be waited on but doesn't resolve quickly enough to hang the process
const now=performance.now() //noticable performance difference when checking time outside loop
for(let i=0;i<timers.length;i++){
if(!timers[i]) continue;
const timer=timers[i];
if(now-timer.start >= timer.ms){
timer.userFN();
if(!timer.repeat){
delete timers[i];
timers.queue.add(i);
timers.count--;
}
else timer.start=now;
}
}
}
let active=false;
function activate(){
if(active) return null;
sender=new BroadcastChannel(channel);
receiver=new BroadcastChannel(channel);
receiver.addEventListener('message',listener);
sender.postMessage(null);
active=true;
}
function deactivate(){
if(!active) return null;
receiver.removeEventListener('message',listener);
receiver.close();
sender.close();
active=false;
}
//timer engine end
function timeout(userFN,ms){
activate();
return randomChannel({userFN,ms,repeat:false,start:performance.now()});
}
function interval(userFN,ms){
activate();
return randomChannel({userFN,ms,repeat:true,start:performance.now()});
}
async function wait(ms){
let resolver=null, promise=new Promise(resolve=>resolver=resolve);
timeout(resolver,ms);
return await promise;
}
function clear(ID){
if(typeof ID!=="number" || !timers[ID]) return false;
delete timers[ID];
timers.queue.add(ID);
timers.count--;
return true;
}
//exports
const timer={timeout,interval,wait,clear};
if(typeof window!=="undefined") window.timer=timer; //browser
else module.exports=timer; //nodejs
})(16)