Repository: microsoft/PowerToys
File: src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/ListViewModel.cs (C#)

Code before:
                catch (Exception ex)
                {
                    ShowException(ex, _model?.Unsafe?.Name);
                }
            });
        }
        else
        {
            // But for all normal pages, we should run our fuzzy match on them.
            lock (_listLock)
            {
                ApplyFilterUnderLock();
            }

            ItemsUpdated?.Invoke(this, EventArgs.Empty);
            UpdateEmptyContent();
            _isLoading.Clear();
        }
    }

    //// Run on background thread, from InitializeAsync or Model_ItemsChanged
    private void FetchItems()
    {
        // TEMPORARY: just plop all the items into a single group
        // see 9806fe5d8 for the last commit that had this with sections
        _isFetching = true;

        try
        {
            var newItems = _model.Unsafe!.GetItems();

            // Collect all the items into new viewmodels
            Collection<ListItemViewModel> newViewModels = [];

            // TODO we can probably further optimize this by also keeping a
            // HashSet of every ExtensionObject we currently have, and only
            // building new viewmodels for the ones we haven't already built.
            foreach (var item in newItems)
            {
                ListItemViewModel viewModel = new(item, new(this));

                // If an item fails to load, silently ignore it.
                if (viewModel.SafeFastInit())
                {
                    newViewModels.Add(viewModel);
                }
            }

            var firstTwenty = newViewModels.Take(20);
            foreach (var item in firstTwenty)
            {

Review comment (suggestion):
It's possible that two `RaiseItemChanged` events might arrive out of order—for instance, the first event could be delayed by some unexpected RPC issue and arrive after the second event, then the second event will be canceled, causing the first event to be used to refresh the UI.

Code after:
                }
                catch (Exception ex)
                {
                    ShowException(ex, _model?.Unsafe?.Name);
                }
            });
        }
        else
        {
            // But for all normal pages, we should run our fuzzy match on them.
            lock (_listLock)
            {
                ApplyFilterUnderLock();
            }

            ItemsUpdated?.Invoke(this, EventArgs.Empty);
            UpdateEmptyContent();
            _isLoading.Clear();
        }
    }

    //// Run on background thread, from InitializeAsync or Model_ItemsChanged
    private void FetchItems()
    {
        // Cancel any previous FetchItems operation
        _fetchItemsCancellationTokenSource?.Cancel();
        _fetchItemsCancellationTokenSource?.Dispose();
        _fetchItemsCancellationTokenSource = new CancellationTokenSource();

        var cancellationToken = _fetchItemsCancellationTokenSource.Token;

        // TEMPORARY: just plop all the items into a single group
        // see 9806fe5d8 for the last commit that had this with sections
        _isFetching = true;

        try
        {
            // Check for cancellation before starting expensive operations
            cancellationToken.ThrowIfCancellationRequested();

            var newItems = _model.Unsafe!.GetItems();

            // Check for cancellation after getting items from extension
            cancellationToken.ThrowIfCancellationRequested();

            // Collect all the items into new viewmodels
            Collection<ListItemViewModel> newViewModels = [];

            // TODO we can probably further optimize this by also keeping a
            // HashSet of every ExtensionObject we currently have, and only
            // building new viewmodels for the ones we haven't already built.