Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining async methods

I'm attempting to chain together a few async methods I've created and I believe there is some fundamental misunderstanding on my part about how this works

Here's a representation of my code:

public async Task<bool> LoadFoo()
{
    return await Foo.ReadAsync("bar").ContinueWith((bar) =>
    {
        Foo.ReadAsync("baz").ContinueWith((baz) =>
        {
            Foo.ReadAsync("qux").ContinueWith((qux) =>
            {
                return true;
            });

            return true;
        });

        return true;
    });
}

public void LoadEverything()
{
    LoadFoo().ContinueWith((blah) =>
    {
        OtherLoadMethod();
    });
}

Now I was expecting when LoadEverything() was called that all of the ReadAsync methods in LoadFoo ("bar", "baz" and "qux") would run and complete, and after they all completed then the .ContinueWith in LoadEverything would run so that OtherLoadMethod() wouldn't execute until the "bar", "baz" and "qux" ReadAsync methods finished.

What I am actually seeing is that LoadFoo gets called and then OtherLoadMethod starts to run before getting to the final completion in LoadFoo (the ContinueWith of the "qux" ReadAsync).

Can someone help clear up my misunderstanding here? Why wouldn't the execution of OtherLoadMethod wait until ReadAsync("qux") finishes and returns true?

like image 743
JT Smith Avatar asked Dec 18 '22 05:12

JT Smith


1 Answers

Why wouldn't execution of OtherLoadMethod wait until ReadAsync("qux") finishes and returns true?

Because that's how await works. The continuations you register are just that: continuations. They are not executed synchronously in the current method. You are telling the framework that when the current task completes, the continuation should be executed. The Task object returned by ContinueWith() allows you to observe the completion if and when it happens. There would be no need to even return a Task object, if the ContinueWith() method blocked until the continuation was executed.

Likewise, the Task<bool> returned by your LoadFoo() method represents the overall completion of the method, including the await...ContinueWith() that you're returning. The method returns prior to completion of the continuation, and callers are expected to use the returned task if they need to wait for the continuation to complete.

All that said, I don't understand why you're using ContinueWith() in the first place. You obviously have access to await, which is the modern, idiomatic way to handle continuations. IMHO, your code should look something like this (it's not clear why you're returning Task<bool> instead of Task, since the return value is only ever true, but I assume you can figure that part out yourself):

public async Task<bool> LoadFoo()
{
    await Foo.ReadAsync("bar");
    await Foo.ReadAsync("baz");
    await Foo.ReadAsync("qux");

    return true;
}

public async Task LoadEverything()
{
    await LoadFoo();
    await OtherLoadMethod();
}
like image 144
Peter Duniho Avatar answered Jan 02 '23 05:01

Peter Duniho