1

I've seen some posts about async and await and how these actually work, but I'm still a little confused. Suppose I have two async methods and I want to make sure that the second one starts after the first one is finished. For instance consider something like this:

    public async Task MyMethod(Item x)
    {
        await AddRequest(x); // 1. Add an item asynchronously 

        // make sure 2 starts after 1 completes

        await GetAllRequest(); // 2. get all items asynchronously 
    }

Then, what is the proper way of making sure this happens?

Update:

In order to try to provide a Minimal, Complete, and Verifiable example:

I have 2 WCF services in a WPF application to communicate with Oracle WebCenter Content(UCM). Here is the minimal version of my code behind:

UCM Service to add new customer:

    public static async Task<ServiceResult> CheckInCustomer(Customer c)
    {
        CheckInSoapClient client = new CheckInSoapClient();
        using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
        {
                // an async method in Oracle WebContent services to insert new content
                result = await client.CheckInUniversalAsync(c);
        }
        return new ServiceResult();
    }

UCM Service to get all customer:

    public static async Task<Tuple<ServiceResult, QuickSearchResponse>> GetAllCustomers()
    {
        ServiceResult error;
        var result = new QuickSearchResponse();
        SearchSoapClient client = new SearchSoapClient();
        using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
        {
                // an async method in Oracle WebContent services to search for contents
                result = await client.QuickSearchAsync(queryString);
        }
            return new Tuple<ServiceResult, QuickSearchResponse>(error, result);
    }

Add Customer Async method which is binded to a Button's command in UI:

    private async Task AddCustomer()
    {
        var result = await CheckInCustomer(NewCustomer);
        if (!result.HasError)
            await GetAllCustomers();
    }

    public ICommand AddCustomerCommand
    {
        get
        {
            _addCustomerCommand = new RelayCommand(async param => await AddCustomer(), null);
        }
    }

Get all Customer Async method (Items is binded to a DataGrid in UI):

    private async Task GetAllCustomers()
    {
        Items.Clear();
        var searchResult = await GetCustomersInfoAsync();
        if (!searchResult.HasError)
        {
            foreach (var item in searchResult)
                Items.Add(new CustomerVm(item));
        }
    }

Now, when I add a new Customer, I expect to see the newly created item in the DataGrid as I first insert the customer and then get all the customers. But this code behaves in a random manner, meaning that sometimes the list shows the newly created customer a few seconds after the insertion and sometimes doesn't.

Bahman_Aries
  • 4,658
  • 6
  • 36
  • 60
  • 2
    After using `await` you will not recieve a task, but the result of the task. `await` will _asynchronously wait_ until the task is finished. – FCin Jan 06 '18 at 08:49
  • As I know `await` doesn't wait for the associated call to be completed, this is better to use `Wait()` if you want ensure `t1` be completed and then `t2` started. – Aria Jan 06 '18 at 08:58
  • 1
    @Aria Not true. `await` as the name suggests _asynchronously waits_ for task to finish. If you don't believe me see [this example](http://rextester.com/ATUB19368) – FCin Jan 06 '18 at 09:23
  • @FCin: what do you mean by asynchronously waiting? – Bahman_Aries Jan 06 '18 at 09:24
  • @Bahman_Aries I'm mean that your tasks will be executed one after another without blocking. This means you don't lose responsiveness of your application. – FCin Jan 06 '18 at 09:28
  • @FCin: what I dont understand here is that `asynchronous` means the first method can be left unfinished and the execution of the main task continues. So it's possible that some parts of the second method runs before the completion of the first method. – Bahman_Aries Jan 06 '18 at 09:32
  • @FCin This is actually what's happening at my code right now, as sometimes the second method runs before the first one. – Bahman_Aries Jan 06 '18 at 09:33
  • Second method cannot run before first one in the provided example, so you should search for another source of behavior you observe – Evk Jan 06 '18 at 09:37
  • @Bahman_Aries provide the code of your AddRequest func – Hey24sheep Jan 06 '18 at 09:47
  • Could be a race condition? @FCin – Hey24sheep Jan 06 '18 at 09:50
  • Please show us the parts of `AddRequest()` and `GetAllRequest()` that start the asynchronous processing. – abto Jan 06 '18 at 09:55
  • We need to see how `AddRequest` and `GetAllRequest` are implemented. – FCin Jan 06 '18 at 09:56
  • @FCin, I've updated my question. – Bahman_Aries Jan 06 '18 at 10:12
  • Did you try stepping through your code with debugger? At which point `CheckInCustomer` returns? – FCin Jan 06 '18 at 10:31
  • @FCin the strange thing is that when I proceed with the debugger, It always works as expected. I'm assuming the debugger gives enough delay for the `CheckInCustomer` to add a new customer. But when I run the code with no interruption, the results become random. – Bahman_Aries Jan 06 '18 at 10:43

2 Answers2

4

The code your provide actually does what you want.

public async Task MyMethod(Item x)
{
    await AddRequestAsync(x); 

    await GetAllRequestAsync(); 
}

But if you are talking about the unpredictable "very very short" delay between them and you want to make sure there is no delay, it is impossible.

Second Person Shooter
  • 14,188
  • 21
  • 90
  • 165
  • Thanks for the answer, But this doesn't work for me as right now, the second line sometimes runs first. If I understand `await` correctly, it schedules the run time of remaining parts of the method (i.e. `AddRequestAsync`) for another time and then returns the control to the calling method (which is `MyMethod`). So parts of the second Method (`GetAllRequestAsync`) can actually runs before the completion of the first method. is that right? – Bahman_Aries Jan 06 '18 at 09:21
  • 2
    @Bahman_Aries: No, that's not correct. If you believe that's what you're seeing, you should provide a [mcve] in your question. – Jon Skeet Jan 06 '18 at 09:30
  • 1
    _"The second line sometimes runs first"_ - this must be a false conclusion. If you mean that `GetAllRequest` does not contain the previously added request it is another story. Maybe `AddRequest` works in fire and forget mode and returns before actually processing the request. But it's really hard to tell without seeing the actual implementation. – György Kőszeg Jan 06 '18 at 09:56
  • @taffer, I'm sure you are right about the false conclusion, But I dont know what I'm doing wrong. I've provided some more details. – Bahman_Aries Jan 06 '18 at 10:11
  • @Bahman_Aries: You haven't provided a [mcve] though. Can we copy/paste/compile/run that code? Nope. – Jon Skeet Jan 06 '18 at 10:13
  • @JonSkeet, I'm sorry but providing a code which can be compiled and run here is impossible, for starter, it needs Oracle WebLogic Server to be up and running. But I've tried to show my code behind as much as possible. If it's still vague, I can provide more details. – Bahman_Aries Jan 06 '18 at 10:17
  • 1
    @Bahman_Aries: I disagree. You just need to take the database parts out and replace them with hard-coded data. This sort of "divide and conquer" to work out where the problem lies is a core part of software engineering - and *definitely* a core part of presenting a good question on Stack Overflow. – Jon Skeet Jan 06 '18 at 10:20
  • @taffer, It seems that what you have mentioned about `AddRequest` working in *fire and forget* mode, is the only logical explanation here. It appears the method `CheckInUniversalAsync` provided by Oracle server only guarantees a "checkin" and nothing more. Thanks a lot for the kind comments and answers. – Bahman_Aries Jan 06 '18 at 12:30
3

Await - (preferred, must be used over ContinueWith)(TY to @Taffer and @Michael comments) waits for newly created task to finish and ensures continuation once execution of waiting task is finished.

Your first func AddRequest will provide the result before moving to GetAllRequest.

public async Task MyMethod(Item x)
{
    await AddRequest(x);
    await GetAllRequest();
}

what I don't understand here is that asynchronous means the first method can be left unfinished and the execution of the main task continues. So it's possible that some parts of the second method runs before the completion of the first method.

It’s called asynchronous programming because runtime capture state of program when it encounter await keyword (which is similar to yield in iterator) and restore state back once waited task finish so the continuation runs on correct context..

But if you want to run it only after first task this is something you could do.

Use ContinueWith - its method available on the task which allows executing code after task finished execution. In simple word it allows continuation.

public async Task MyMethod(Item x)
{
    var req = AddRequest(x).ContinueWith(async (t1) =>
               {
                  await GetAllRequest();
               }); 
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Hey24sheep
  • 1,172
  • 6
  • 16