Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to complete a Task from another function?

I want to query data from a database via a webservice. The provided API allows to send batch requests, so that multiple requests are sent in a single HTTP request. I'd like to make benefit from this and am trying to build a C# interface to use this service using async code and Tasks.

My goal is to achieve something like the following:

// Service class that abstracts the server interface.
class Service
{
    // Return all StateA objects at the next server contact.
    public Task<IEnumerable<StateA>> GetStatesA() { ... }
    // Return all StateB objects at the next server contact.
    public Task<IEnumerable<StateB>> GetStatesB() { ... }
    // Send all queued requests to the server.
    public Task SendRequests() { ... }
}

Usage of the API:

// Query all StateA and StateB objects from the database and
// process them as soon as they arrive.
service.GetStatesA().ContinueWith((t) => DoSomething(t.Result));
service.GetStatesB().ContinueWith((t) => DoSomething(t.Result));

// Some other code

// Actually access the API such that the data is queried and the Tasks completed.
service.SendRequests();

From my knowledge, Tasks in C# can only wrap synchronous functions and return when they finish. Is there any way to complete a Task from the outside?

I'm looking for something similar to e.g. the Completer class in Dart (see https://api.dartlang.org/1.13.0/dart-async/Completer-class.html).

like image 917
Dominik Avatar asked Oct 18 '25 21:10

Dominik


1 Answers

What you're looking for is a TaskCompletionSource<T>:

public class Foo
{
    private readonly TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    public Task GetStateA()
    {
        // Do stuff;
        return tcs.Task;
    }

    public Task GetStateB()
    {
        //Do stuff
        return tcs.Task;
    }

    public async Task QueryApiAsync()
    {
        // Query the API
        tcs.SetResult(true);
    }
}

Although possible to use, it looks to me like the API you're exposing isn't really convenient. I wouldn't want GetStateA() to return a Task which only completes once the batch query executes. I would rather have a some sort of batch method which is provided with an IEnumerable<States> and returns to the caller once the batch completes. If you're not really planning on allowing a single state to query the API.

So, I would either turn the API methods that has an Enqueue method which invokes a callback:

public interface Foo
{
    void Enqueue<T>(T state, Action callback) where T : State;
}

Or actually expose the functionality of making a singular call to the API, as well as a Batch operation:

public interface Foo
{
    Task<List<StateA>> GetStatesAAsync();
    Task<List<StateB>> GetStatesBAsync();
    Task<List<IState>> GetBatchStatesAsync(IEnumerable<IState> states);
}
like image 140
Yuval Itzchakov Avatar answered Oct 21 '25 12:10

Yuval Itzchakov