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/@ramu-narasinga
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)