-
Notifications
You must be signed in to change notification settings - Fork 0
/
event-loop-monitor.js
118 lines (95 loc) · 2.55 KB
/
event-loop-monitor.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { EventEmitter } from 'events';
import { AsyncInterceptor } from './async-interceptor';
function isNode () {
return typeof process !== 'undefined' && process.versions && process.versions.node;
}
function polyfillNow () {
const [seconds, nanoseconds] = process.hrtime();
return seconds * 1000 + nanoseconds / 1000000;
}
export class EventLoopMonitor extends EventEmitter {
lagging = false;
constructor (timeoutMillis) {
super();
this.timeoutMillis = timeoutMillis;
this._watchLag = this._watchLag.bind(this);
this._stopped = true;
this._startTime = null;
this._totalLag = 0;
this._registerNowFunc();
}
start () {
this._stopped = false;
this._lastWatchTime = null;
this._startTime = Date.now();
this._totalLag = 0;
this.on('lag', this._watchLag);
this._detectLag();
}
stop () {
this._stopped = true;
this.removeAllListeners('lag');
}
status () {
let pctBlock = 0;
let elapsedTime = 0;
if (!this._stopped && this._lastWatchTime) {
elapsedTime = this._lastWatchTime - this._startTime;
pctBlock = (this._totalLag / elapsedTime) * 100;
}
let statusObject = {
pctBlock,
elapsedTime,
totalLag: this._totalLag
};
this._startTime = this._lastWatchTime;
this._totalLag = 0;
return statusObject;
}
_watchLag (lag) {
this._lastWatchTime = Date.now();
this._totalLag += lag;
}
_detectLag () {
let self = this;
let start = self._now();
setTimeout(() => {
let end = self._now();
let elapsedTime = end - start;
let realDiff = elapsedTime - self.timeoutMillis;
let lag = Math.max(0, realDiff);
if (lag >= 100) {
console.log('lag', lag);
this.lagging = true;
AsyncInterceptor.enable()
} else {
this.lagging = false;
AsyncInterceptor.disable()
}
if (!self._stopped) {
self.emit('lag', lag);
self._detectLag();
}
}, self.timeoutMillis);
}
_registerNowFunc () {
if (isNode()) {
const [major] = process.versions.node.split('.').map(Number);
if (major < 8) {
this._now = polyfillNow;
return;
}
const {
performance
// eslint-disable-next-line global-require
} = require('perf_hooks');
this._now = performance.now.bind(performance);
return;
}
if (typeof window !== 'undefined' && window.performance && window.performance.now) {
this._now = window.performance.now;
return;
}
this._now = Date.now;
}
}