1

I'm currently building a media feed in C# Xamarin.

The media feed is an observablecollection of data models for individual posts.

The API I'm using is fairly limited, so I can't properly query to specify the data I want, which means I end up with a lot of data. To make sure users actually get enough content, my collection needs to request anywhere between 100 and 500 elements.

This makes loading times extremely long, and I wish to yield return the collection to continuously update the media feed while retreiving data from the API.

The feed manager

 public static async Task<ObservableCollection<PostPresenter>> GetFeed(ApiClient client, string account, int limit = 100)
    {
        var feed = new ObservableCollection<PostPresenter>();

        var feedResult = await ApiClient.FeedClient.GetFeed(account, limit: limit);

        if (feedResult != null)
        {
            foreach (var feedObj in feedResult.Result)
            {


                if (feedObj != null)
                {

                    var comment = feedObj;
                    feed.Add(new PostPresenter()
                    {
                        Username = comment.Author,
                        Description = comment?.Title ?? comment.Body?.TruncateString(15) ?? "",
                        Media = comment.JsonMetadata.Image != null ? comment?.JsonMetadata?.Image.FirstOrDefault() : null,
                        Tags = comment?.JsonMetadata?.Tags.ToList() ?? new List<string> { "" },
                        PostTime = comment?.Created.ToString(),
                        Permlink = comment.Permlink,
                        EntryId = feedObj.Id


                    });



                }

            }
        }
        foreach (var testObj in feed)
        {
            await GetFeedMetadata(client, testObj);
        }

        return feed;
    }

The mainpage/feed page

 var tempFeedEntries = await FeedManager.GetFeed(User.ApiClient, User.Username);
                //var tempFeedEntries = await FeedManager.GetAllFeedData(User.ApiClient, Feed);
                if (tempFeedEntries != null)
                {
                    foreach(var tempFeedEntry in tempFeedEntries)
                    {
                        Feed.Add(tempFeedEntry);
                        IsLoading = false;
                        FeedListViewLoading.IsRunning = false;
                        FeedListViewLoading.IsRunning = false;

                        await FeedManager.GetAllFeedData(User.ApiClient, tempFeedEntries);
                        FeedListView.ItemsSource = Feed;
                    }

                }

How can I return a temporary copy of the collection or posts to fill the feed without waiting for every element to be loaded?

sflovik
  • 77
  • 9
  • What is this doing `await GetFeedMetadata(client, testObj);` is this some action that needs to be done on each result, is this another api call? does it add to the PostPresenter or modify it in some way? – TheGeneral Mar 04 '19 at 02:05
  • Hi, sorry forgot to clip that part, it's not in use. – sflovik Mar 04 '19 at 15:15

1 Answers1

2

There are many things you could do here, however the one thing you can't do is use yield in an async method.

I would suggest using TPL DataFlow,. Basically Dataflow allows you construct pipeline processors.

You can create a job that returns information for other jobs that returns information for other jobs so-on-and-so-forth.

The advantages are,

  • It works well with async and await which means you can very effectively parallel this task.

  • Because these are seemingly IO workloads, it will efficiently use IO completion ports and not the threadpool to scale this very efficiently .

  • You could envisage setting the max degree of parallelism to allow this to be broken up 10's to 100's (or more) times without any extra code or overhead, in-turn getting all the data from your feed (and metadata) all simultaneously without holding up threads.

  • You can specify it to ensure order, which means it will work in parallel and also return the results for the next processor in the pipeline sequentially.

  • You could end it with an action block (unthreaded), which can update your ObserverableCollection with AddRange (though I forget if that method will notify), also you will need to marshal that call back to your ui thread using Invoke.

Even with the downside of marshalling, DataFlow would make this more efficient and more responsive by several factors with very minimal code.

Unfortunately, there is just not enough information about your types, API, and environment for me to put together even a small example. However, I truly suggest you push through the learning curve and learn this valuable TPL resource and library.

halfer
  • 19,824
  • 17
  • 99
  • 186
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • Thank you for all the information, this looks like an excellent resource. I'll have a look at this! – sflovik Mar 04 '19 at 15:16