Q:

Using TaskCompletionSource Within An await Task.Run Call

I am getting unexpected behavior that I would like to shed some light on.  I've created a simple example to demonstrate the problem.  I call an async function using Task.Run, which will continuously generate results, and uses IProgress to deliver updates to the UI.  But I want to wait until after the UI actually updates to continue, so I tried using TaskCompletionSource as suggested in some other posts (this seemed somewhat similar: Is it possible to await an event instead of another async method?.)  I'm expecting the initial Task.Run to wait, but what is happening is the await happening inside seems to move it onward and "END" happens after the first iteration. Start() is the entry point:
public TaskCompletionSource<bool> tcs;

public async void Start()
{
    var progressIndicator = new Progress<List<int>>(ReportProgress);

    Debug.Write("BEGIN\r");
    await Task.Run(() => this.StartDataPush(progressIndicator));
    Debug.Write("END\r");
}

private void ReportProgress(List<int> obj)
{
    foreach (int item in obj)
    {
        Debug.Write(item + " ");
    }
    Debug.Write("\r");
    Thread.Sleep(500);

    tcs.TrySetResult(true);
}

private async void StartDataPush(IProgress<List<int>> progressIndicator)
{
    List<int> myList = new List<int>();

    for (int i = 0; i < 3; i++)
    {
        tcs = new TaskCompletionSource<bool>();

        myList.Add(i);
        Debug.Write("Step " + i + "\r");

        progressIndicator.Report(myList);

        await this.tcs.Task;
    }
}

With this I get:
BEGIN
Step 0
0 
END
Step 1
0 1 
Step 2
0 1 2 

instead of what I want to get which is:
BEGIN
Step 0
0 
Step 1
0 1 
Step 2
0 1 2 
END

I'm assuming I am misunderstanding something about Tasks and await and how they work. I do want StartDataPush to be a separate thread, and my understanding is that it is. My end use is somewhat more complex as it involves heavy calculation, updating to a WPF UI and events signaling back that it completed, but the mechanics are the same. How can I achieve what I'm trying to do?

A:

I'm not fully understanding the goal you are trying to achieve.  But the issue is StartDataPush returning void.  The only time an async should return void is if it is an event handler otherwise it needs to return Task.
The following would achieve what you expected in terms of output
public partial class MainWindow : Window
{
    public TaskCompletionSource<bool> tcs;

    public MainWindow()
    {
        InitializeComponent();
    }

    private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var progressIndicator = new Progress<List<int>>(ReportProgress);

        Debug.Write("BEGIN\r");
        await StartDataPush(progressIndicator);
        Debug.Write("END\r");
    }

    private void ReportProgress(List<int> obj)
    {
        foreach (int item in obj)
        {
            Debug.Write(item + " ");
        }
        Debug.Write("\r");
        Thread.Sleep(500);

        tcs.TrySetResult(true);
    }

    private async Task StartDataPush(IProgress<List<int>> progressIndicator)
    {
        List<int> myList = new List<int>();

        for (int i = 0; i < 3; i++)
        {
            tcs = new TaskCompletionSource<bool>();

            myList.Add(i);
            Debug.Write("Step " + i + "\r");

            progressIndicator.Report(myList);

            await this.tcs.Task;
        }
    }
}

