The caching logic is slightly flawed. During the time the http request is in progress, foo is left unpopulated, and if another method calls GetFooDataAsync() during this time, the http request will be repeated.
Ideally the cache should populate only once.
To ensure one and only one invocation of the task, cache the task itself, not the result, and simply await it whenever you want to get the result. A second or third await won't invoke the method again, it'll simply access the existing result.