In old-style await'less NodeJS, when you call a function that had an asynchronous aspect, you'd pass in the well-known callback which gets called when the asynchronous portion is done. This didn't pollute the return value of the function whereas the function can still return something meaningful to the caller synchronously.

The problem with the async/await pattern now is that to use it, you have to decide if a function is synchronous or asynchronous. This means that if I have an existing function that's synchronous, and has a return value that will remain synchronously calculated and thus returned when the function returns, and I want to add an asynchronous component to it, previously I would add a callback, and change the callers. But now I have to make the return value the asynchronous value and the original return value --- find another way. What would that be?

Would you use an 'out' parameter in C#? That's not a best-practice. Would you pass in an object whose state you modify to return the sync value?

The current trend is return a tuple, and object with multiple values. But can one of those values by synchronous and the other asynchronous?

  • 1
    an async method returns a Task in C# and a Promise in JavaScript - you could just use that and pass in a callback "the old way". In C# you can also block with a Task, although that can result in deadlock if you don't take precautions - blog.stephencleary.com/2012/07/dont-block-on-async-code.html – Ben Cottrell Aug 4 at 10:39
  • can you give an example of the NodeJS thing that you say is problematic with async Task? I'm not sure i follow – Ewan Aug 4 at 11:43
  • You must have rightful reasons to do it, would you explain them to us? I mean, why cannot you have those features in different methods? What are the differences between the values you can return sync and the values you have to return async? – A Bravo Dev Aug 4 at 12:18
  • Ben, I think you are not addressing what I'm saying. In C# when you return the Task, that means that you have to wait for the Task to complete to get the result out of the Task. But I'm saying the function has something to return immediately, not later. I want to return a value immediately, and I want to return a Task/Promise for the value to be returned in the future. if I return both from the method, and I use a Tuple, then I can't use the async/await keywords. – Daisha Lynn Aug 8 at 21:36
  • 1
    I think it's slightly confusing for the reader. Any specific reason you can't 1) split that in two methods (one async) or 2) simply await and you have both values together? – Adriano Repetti Aug 9 at 6:20

Why can't you return an already completed task?

C#: return Task.FromResult(...);

JavaScript: return Promise.resolve(...);

In fact, C# recently added ValueTask<T> to avoid allocations for this scenario where a value is available immediately.

  • What do you do about the asynchronous part? The OP has asked about returning a synchronous result and an asynchronous result from the same method. Your answer resolves the synchronous part of the result, but not the asynchronous part. – Robert Harvey Aug 9 at 15:35
  • As I understand it, ValueTask is awaitable, so it is transparent to the consumer which result is used, synchronous or asynchronous. Within the ValueTask returning method, you can still return an asynchronous result if necessary (if the synchronous result is not available) by providing a Task to ValueTask. Maybe I am not understanding the question. – Matt H Aug 10 at 16:27

The current trend is return a tuple, and object with multiple values. But can one of those values by synchronous and the other asynchronous?

surely you can return it. Tuple<Foo, Task<Bar>>. You can create it like this:

// this is a function starting a task producing Bar
Task<Bar> GetBarAsync() {
    ...
}

...

Tuple<Foo, Task<Bar>> Baz()
{
    return Tuple.Create(
        new Foo(),
        GetBarAsync());
}

However, as this is SE, I would add that I don't clearly undestand why would you need such function. It clearly mixes concerns to me. Asynchronous function does not mean to be doing anything at the moment of call. It does something, in fact, but it does not matter in a typical scenario.

  • Why i would do it? Well, let's say I have a synchronous method that returns a "Book" object. That's its purpose. Now I'm modifying the code to make some HTTP/REST code to acquire some information about the book. Let's say footnotes, but the client doesn't need that information right now. They can use the Book with no problem, and rarey do they need to look at the footnotes. But for a client that does care, he wants to wait for the task to complete. How to return the task to the caller as well as the book. – Daisha Lynn Aug 8 at 21:41
  • @DaishaLynn then why not make it two calls? Or have the footnotes be a method/property of the book object (depending on exactly how you want to structure your code, two calls likely has better Separation of Concerns.). – NPSF3000 Aug 9 at 13:32

Why would you want this at all? Sticking to your example with the Book and the Footnotes, where one is fetched synchronously and the other one is fetched asynchronously, it should really be two methods (or at least two method calls), instead of one, since fetching the Book synchrnously and fetching the Book with footnotes asynchronously are two slightly different things.

From your comment to the other answer:

But for a client that does care, he wants to wait for the task to complete. How to return the task to the caller as well as the book.

You could add a parameter to specify what you'd like to fetch. Only the metadata (for the sake of your question we'd assume that the metadata would be returned quickly) or extended data (which won't be returned that quickly)

public Book FetchBook(FetchBookOptions options);
public Task<Book> FetchBookAsync(FetchBookOptions options);

[Flags]
public enum FetchBookOptions
{
    None,
    Metadata = 1,
    Content = 2,
    Footnotes = 4,
    All = Metadata | Content | Footnotes
}

The client that cares for the metadata, the footnotes and the content, but needs to stay responsive, may call

var book = await bookRepository.FetchBookAsync(FetchBookOptions.All);

The client that dows not make use of async/await, but needs all data may call

 var book = await bookRepository.FetchBookAsync(FetchBookOptions.All);

and the client that only needs the metadata, which is known to be provided "immediately" may either call

var book = await bookRepository.FetchBookAsync(FetchBookOptions.Metadata);

or

var book = await bookRepository.FetchBookAsync(FetchBookOptions.Metadata);

Eventually you coud do somthing like this

private async void Button_OnClick()
{
    this.SetBooks(await bookRepository.FetchBookAsync(FetchBookOptions.Metadata));
    this.UpdateBooksFootnotes(await bookRepository.FetchBooksAsync(FetchBookOptions.All));
}

This achieves exactly what you want, setting the original books immediately - given that FetchBookOptions.Metadata returns immediately, or at least very quickly - and as soon as the footnotes have been fetched, you can update them.

The current trend is return a tuple, and object with multiple values. But can one of those values by synchronous and the other asynchronous?

Yes, quite simply Tuple<Foo, Task<Bar>>.

But you maybe don't want that. Instead, return both asynchronously.

async Task<Tuple<Foo, Bar>> getFooBarAsync()
{
    var bar = getBarAsync();
    var foo = getFoo();
    return new Tuple<Foo, Bar>(foo, await bar);
}

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.