as

Settings
Sign out
Notifications
Alexa
Amazon Appstore
AWS
Documentation
Support
Contact Us
My Cases
Get Started
Design and Develop
Publish
Reference
Support

Investigate JavaScript Thread Performance

Thread state visualization lets you visualize what states the threads are in during an app lifecycle. It can help you troubleshoot and optimize the performance of your JavaScript (JS) apps.

To use thread state visualization effectively, follow the guidance on this page.

Understand thread states

Threads can exist in various states, such as Running, Runnable, Runnable (Preempted), Sleeping, Uninterruptible Sleep, Stopped, Idle, and Exit. For exact definition of each state, see Thread States in Monitor and Record Your App Performance.

Identify causes of performance issues

Thread state visualization can provide insight into an apps performance issues, such as increased latency, unresponsiveness, crashes, and inconsistent behavior. The following table lists some of the possible causes of performance issues that can be identified using thread state information.

Cause Description Related thread states
Long-running threads Threads that remain in a "Running" state for extended periods may indicate inefficient processing or long-running operations that could benefit from optimization. Running
High CPU usage A situation where many threads are actively in a "Running" state, which can suggest inefficient algorithms or excessive context switching, leading to resource strain. Running, Runnable
CPU contention High levels of contention occur when multiple threads compete for limited CPU resources. This can lead to threads being preempted or waiting for access to shared resources. Runnable (Preempted)

Use Vega Studio Performance Extension

Vega Studio Performance Extension (KSPE) provides thread state visualization in the Recording view of the Activity Monitor and Memory Monitor. To troubleshoot performance issues in your JS apps, complete the following steps:

  1. Start a recording in the Activity Monitor.
  2. Replicate the problematic behavior in the app.
  3. Stop the recording to generate the recording view, which includes the Traces view with thread state information.

Analyze thread state data

To identify app performance issues:

  1. Analyze the thread state data in Traces view.
  2. Examine if the thread state data corresponds to any of the previously mentioned causes: long-running threads, high CPU usage or CPU contention.

Use Cases

The following examples show how thread state visualization can help troubleshoot and optimize app performance.

Use case 1: Troubleshoot long-running threads due to inefficient algorithms

While testing VegaVideoApp, you experienced poor responsiveness issues when pulling up the Home Screen or any detail pages.

To identify the issue, you started a recording in the Activity Monitor while replicating the problematic behavior in the app. When the recording stopped, the Activity Monitor generated a recording view, including the Traces view with thread state information. You examined the Traces, which showed JS Thread was running for up to 5 seconds. This indicated long-running thread due to inefficient algorithms.

Traces view showing JS Thread in running state for 5 seconds, indicating long-running thread performance issue
Thread state visualization reveals long-running JS Thread issue

You examined the CPU Profiler Flamegraphs, where the DetailsScreen was taking 5 seconds due to the getClassics method.

CPU Profiler Flamegraph highlighting DetailsScreen taking 5 seconds due to inefficient getClassics method
Flamegraph identifies getClassics method as performance bottleneck

While revisiting your app code, you noticed inefficient code while sorting and filtering the data, causing performance lag issue.

 export const getClassics = (): TitleData[] => {
   const classics = [
     {
       id: '169327',
       title: 'Ethereal Echos',
       description: 'Lorem ipsum dolor sit amet',
       mediaType: 'video',
       mediaSourceType: 'url',
       duration: 300,
       thumbnail: tile09,
       posterUrl: tile09,
       videoUrl:
         'https://edge-vod-media.cdn01.net/encoded/0000169/0169313/video_1880k/T7J66Z106.mp4?source=firetv&channelID=13454',
       categories: ['Hits'],
       channelID: '13455',
       rating: '5',
       releaseDate: '09/20',
       format: 'MP4',
       uhd: false,
       secure: false,
       rentAmount: '200',
     },
     ...
     ...
   ];

   // Inefficient sorting
   const sortClassics = (array: TitleData[]): TitleData[] => {
     const n = array.length;
     for (let i = 0; i < n; i++) {
       for (let j = 0; j < n - i - 1; j++) {
         for (let k = 0; k < n; k++) {
           if (array[k].title === 'NA') {
             continue;
           }
         }

         if (array[j].title.localeCompare(array[j + 1].title) > 0) {
           // Swap if the current element is greater than the next
           const temp = array[j];
           array[j] = array[j + 1];
           array[j + 1] = temp;
         }
       }
     }
     return array;
   };

   // Inefficient filtering using nested loops
   const filterClassics = (array: TitleData[], category: string): TitleData[] => {
     const result: TitleData[] = [];

     for (let i = 0; i < array.length; i++) {
       let found = false;
       for (let j = 0; j < array.length; j++) {
         if (array[i].categories.includes(category) && array[i] === array[j]) {
           found = true;
           break; 
         }
       }
     }

     return result;
   };

   const sortedClassics = sortClassics(classics);

   const filteredClassics = filterClassics(sortedClassics, 'Hits');

   return filteredClassics;
 };

To resolve the issue, you can remove the unnecessary nested loops. Then, revisit the Traces view to validate if your app performance improves.

Traces view showing improved performance with reduced JS Thread running time after code optimization
Thread state shows performance improvement after removing inefficient code

Use case 2: Troubleshoot high CPU usage in a scrolling app

You created a simple scrolling app using the React Native Flatlist component to display 200 rows with different colors. Using the Activity Monitor, you observed frequent "running" states in the thread state view during scrolling, indicating high CPU usage. The CPU Profiler Flamegraphs also showed high activity, suggesting the Flatlist component wasn't functioning efficiently.

Thread state visualization and CPU Profiler showing frequent running states and high activity during FlatList scrolling
High CPU usage detected during FlatList scrolling performance

The following example code shows how Flatlist component was added in the app.

 const PerfFlatListApp = () => {
     // https://reactnative.dev/docs/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem
     const renderItem = useCallback(
         ({ item, index }: { item: FlatListItem; index: number }) => (
             // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-basic-components
             <Item
                 key={item.title}
                 buttonStyle={[
                     styles.item,
                     {
                         backgroundColor: calculateBackgroundColor(index)
                     }
                 ]}
                 hasTVPreferredFocus={item.id === 0}
             />
         ),
         []
     );

     // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-keyextractor-or-key
     const keyExtractor = useCallback(
         (item: FlatListItem, index: number) => item.title,
         []
     );

     // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-getitemlayout
     const getItemLayout = useCallback(
         (
             _data: any,
             index: number
         ): { length: number; offset: number; index: number } => ({
             index,
             length: ITEM_HEIGHT,
             offset: ITEM_HEIGHT * index
         }),
         []
     );

     useEffect(() => {
         console.log('reporting fully_drawn');
         // @ts-ignore
         global.performance.reportFullyDrawn();
     }, []);

     return (
         <View style={styles.container}>
             <FlatList
                 data={data}
                 renderItem={renderItem}
                 // https://reactnative.dev/docs/optimizing-flatlist-configuration#initialnumtorender
                 // explicitly setting it to 10 since that is the initial number of viewable items on screen
                 initialNumToRender={10}
                 keyExtractor={keyExtractor}
                 getItemLayout={getItemLayout}
                 // https://reactnative.dev/docs/optimizing-flatlist-configuration#windowsize,
                 // setting a higher window size on MM than on TV profiles since a quick touch
                 // swipe will scroll the flatlist faster than holding down DPad. Larger window
                 // size will take more memory but will reduce blank ares seen when scrolling.
                 windowSize={Platform.isTV ? 5 : 13}
             />
         </View>
     );
 };

To address the performance issues, replace the Flatlist component with Shopify’s Flashlist component, which is designed for improved performance in scrolling scenarios.

Copied to clipboard.

 const PerfFlashListApp = () => {
     // https://reactnative.dev/docs/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem
     const renderItem = useCallback(
         ({ item, index }: { item: FlashListItem; index: number }) => (
             // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-basic-components
             <Item
                 buttonStyle={[
                     styles.item,
                     {
                         backgroundColor: calculateBackgroundColor(index)
                     }
                 ]}
                 hasTVPreferredFocus={item.id === 0}
             />
         ),
         []
     );

     // https://reactnative.dev/docs/optimizing-flatlist-configuration#use-keyextractor-or-key
     const keyExtractor = useCallback(
         (item: FlashListItem, index: number) => item.title,
         []
     );

     useEffect(() => {
         console.log('reporting fully_drawn');
         // @ts-ignore
         global.performance.reportFullyDrawn();
     }, []);

     return (
         <View style={styles.container}>
             <FlashList
                 estimatedItemSize={ITEM_HEIGHT}
                 data={data}
                 renderItem={renderItem}
                 keyExtractor={keyExtractor}
             />
         </View>
     );
 };

Retest you app's performance, and validate if you see a reduction in the number of "running" states in the thread state view. The CPU Profiler Flamegraphs should also show a decrease in activity.

Thread state visualization showing reduced running states and lower CPU activity after replacing FlatList with FlashList
Performance improvement achieved by switching to FlashList component

To read about the advantages of Flashlist over Flatlist, see Best Practices.



Last updated: Sep 30, 2025