In this article, we review the below code snippet picked from event-tracking.ts
export const startTimingTracking = () => {
const unSubPerformance = setupPerformancePublisher();
const unSubDirtyTaskTracking = startDirtyTaskTracking();
const unSubLongPipelineTracking = startLongPipelineTracking();
const onComplete = async (
_: string,
finalInteraction: FinalInteraction,
event: PerformanceEntryChannelEvent,
) => {
toolbarEventStore.getState().actions.addEvent({
kind: 'interaction',
id: not_globally_unique_generateId(),
data: {
startAt: finalInteraction.detailedTiming.blockingTimeStart,
endAt: performance.now() + performance.timeOrigin,
meta: { ...finalInteraction, kind: event.kind }, // TODO, will need interaction specific metadata here
},
});
const existingCompletedInteractions =
performanceEntryChannels.getChannelState('recording');
finalInteraction.detailedTiming.stopListeningForRenders();
if (existingCompletedInteractions.length) {
// then performance entry and our detailed timing handlers are out of sync, we disregard that entry
// it may be possible the performance entry returned before detailed timing. If that's the case we should update
// assumptions and deal with mapping the entry back to the detailed timing here
performanceEntryChannels.updateChannelState(
'recording',
() => new BoundedArray(MAX_CHANNEL_SIZE),
);
}
};
const unSubDetailedPointerTiming = setupDetailedPointerTimingListener(
'pointer',
{
onComplete,
},
);
const unSubDetailedKeyboardTiming = setupDetailedPointerTimingListener(
'keyboard',
{
onComplete,
},
);
const unSubInteractions = listenForPerformanceEntryInteractions(
(completedInteraction) => {
interactionStore.setState(
BoundedArray.fromArray(
interactionStore.getCurrentState().concat(completedInteraction),
MAX_INTERACTION_BATCH,
),
);
},
);
return () => {
unSubDirtyTaskTracking();
unSubLongPipelineTracking();
unSubPerformance();
unSubDetailedPointerTiming();
unSubInteractions();
unSubDetailedKeyboardTiming();
};
};
Let’s go over this function in chunks.
const unSubPerformance = setupPerformancePublisher();
const unSubDirtyTaskTracking = startDirtyTaskTracking();
const unSubLongPipelineTracking = startLongPipelineTracking();
this calls three functions — setupPerformancePublisher, startDirtyTaskTracking, startLongPipelineTracking.
startDirtyTaskTracking and startLongPipelineTracking are defined in the same event-tracking.ts file except for the setupPerformancePublisher which is imported from core/notifications/performance.ts.
If there is one thing that I noticed, it is the cleanup involved across the react-scan codebase to avoid memory leaks.
onComplete function:
const onComplete = async (
_: string,
finalInteraction: FinalInteraction,
event: PerformanceEntryChannelEvent,
) => {
toolbarEventStore.getState().actions.addEvent({
kind: 'interaction',
id: not_globally_unique_generateId(),
data: {
startAt: finalInteraction.detailedTiming.blockingTimeStart,
endAt: performance.now() + performance.timeOrigin,
meta: { ...finalInteraction, kind: event.kind }, // TODO, will need interaction specific metadata here
},
});
const existingCompletedInteractions =
performanceEntryChannels.getChannelState('recording');
finalInteraction.detailedTiming.stopListeningForRenders();
if (existingCompletedInteractions.length) {
// then performance entry and our detailed timing handlers are out of sync, we disregard that entry
// it may be possible the performance entry returned before detailed timing. If that's the case we should update
// assumptions and deal with mapping the entry back to the detailed timing here
performanceEntryChannels.updateChannelState(
'recording',
() => new BoundedArray(MAX_CHANNEL_SIZE),
);
}
};
this onComplete arrow function is defined in startTimingTracking. There’s an “interaction” event added and then there is this comment explaining what that if block is about:
// then performance entry and our detailed timing handlers are out of sync, we disregard that entry
// it may be possible the performance entry returned before detailed timing. If that's the case we should update
// assumptions and deal with mapping the entry back to the detailed timing here
const unSubDetailedPointerTiming = setupDetailedPointerTimingListener(
'pointer',
{
onComplete,
},
);
const unSubDetailedKeyboardTiming = setupDetailedPointerTimingListener(
'keyboard',
{
onComplete,
},
);
const unSubInteractions = listenForPerformanceEntryInteractions(
(completedInteraction) => {
interactionStore.setState(
BoundedArray.fromArray(
interactionStore.getCurrentState().concat(completedInteraction),
MAX_INTERACTION_BATCH,
),
);
},
);
These 2 functions — setupDetailedPointerTimingListener and listenForPerformanceEntryInteractions are defined in performance.ts
return () => {
unSubDirtyTaskTracking();
unSubLongPipelineTracking();
unSubPerformance();
unSubDetailedPointerTiming();
unSubInteractions();
unSubDetailedKeyboardTiming();
};
This function in the end returns an arrow function that triggers unsubscription/cleanup.
About me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on interesting projects. Send me an email at ramu.narasinga@gmail.com
My Github — https://github.com/ramu-narasinga
My website — https://ramunarasinga.com
My Youtube channel — https://www.youtube.com/@thinkthroo
Learning platform — https://thinkthroo.com
Codebase Architecture — https://app.thinkthroo.com/architecture
Best practices — https://app.thinkthroo.com/best-practices
Production-grade projects — https://app.thinkthroo.com/production-grade-projects
Top comments (0)