-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgetINP.js
121 lines (106 loc) · 3.67 KB
/
getINP.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
119
120
121
const EVERY_N = 50;
const MAX_ENTRIES = 10;
const largestINPEntries = [];
let minKnownInteractionId = Number.POSITIVE_INFINITY;
let maxKnownInteractionId = 0;
function addInteractionEntryToINPList(entry) {
// Add this entry only if its larger than what we already know about.
if (largestINPEntries.length < MAX_ENTRIES || entry.duration > largestINPEntries[largestINPEntries.length-1].duration) {
// If we already have an interaction with this same ID, replace it rather than append it.
let existing = largestINPEntries.findIndex((other) => entry.interactionId == other.interactionId);
if (existing >= 0) {
// Only replace if this one is actually longer
if (entry.duration > largestINPEntries[existing].duration) {
largestINPEntries[existing] = entry;
}
} else {
largestINPEntries.push(entry);
}
largestINPEntries.sort((a,b) => b.duration - a.duration);
largestINPEntries.splice(MAX_ENTRIES);
}
}
function getCurrentINPEntry() {
const interactionCount = estimateInteractionCount();
const which = Math.min(largestINPEntries.length-1, Math.floor(interactionCount / EVERY_N));
return largestINPEntries[which];
}
function updateInteractionIds(interactionId) {
minKnownInteractionId = Math.min(minKnownInteractionId, interactionId);
maxKnownInteractionId = Math.max(maxKnownInteractionId, interactionId);
}
function estimateInteractionCount() {
const drag = performance.eventCounts.get('dragstart');
const tap = performance.eventCounts.get('pointerup');
const keyboard = performance.eventCounts.get('keydown');
// This estimate does well on desktop, poorly on mobile
// return tap + drag + keyboard;
// This works well when PO buffering works well
return (maxKnownInteractionId > 0) ? ((maxKnownInteractionId - minKnownInteractionId) / 7) + 1 : 0;
}
function trackInteractions(callback) {
const observer = new PerformanceObserver(list => {
for (let entry of list.getEntries()) {
// TODO: Perhaps ignore values before FCP
if (!entry.interactionId) continue;
updateInteractionIds(entry.interactionId);
addInteractionEntryToINPList(entry);
callback(entry);
}
});
observer.observe({
type: "event",
durationThreshold: 0, // 16 minumum by spec
buffered: true
});
}
// Will get called multuple times, every time INP changes
export function getINP(callback) {
let previousINP;
trackInteractions(entry => {
const inpEntry = getCurrentINPEntry();
if (!previousINP || previousINP.duration != inpEntry.duration) {
previousINP = inpEntry;
callback({
value: inpEntry.duration,
entries: [inpEntry],
interactionCount: estimateInteractionCount(),
});
}
});
}
// Alternative to getINP
// Will get called multuple times, for every new interaction
// (Note: only when over 16ms duration)
export function reportAllInteractions(callback) {
trackInteractions(entry => {
callback({
value: entry.duration,
entries: [entry],
interactionCount: estimateInteractionCount(),
});
});
}
/* Usage Example */
getINP(({ value, entries, interactionCount }) => {
console.log(`[INP] value: ${value}, interactionCount:${interactionCount}`, entries);
let currentINP = entries[0];
// Thanks philipwalton!
// Add measures so you can see the breakdown in the DevTools performance panel.
performance.measure(`latency`, {
start: currentINP.startTime,
end: currentINP.startTime + currentINP.duration,
});
performance.measure(`delay`, {
start: currentINP.startTime,
end: currentINP.processingStart,
});
performance.measure(`processing`, {
start: currentINP.processingStart,
end: currentINP.processingEnd,
});
performance.measure(`presentation`, {
start: currentINP.processingEnd,
end: currentINP.startTime + currentINP.duration,
});
});