fix: use shield
The code of a cached_property can run more than once in the following situation:
1: current_task awaits a long running method decorated with async_cached_property 2: second_task also awaits the same async_cached_property which has not finished loading, it ends up waiting at the asyncio.Lock 3: current_task gets cancelled for any reason 4: long running coroutine stops executing without completing, side effects might have already occurred 5: second_task is woken up at the asyncio.Lock and again starts the long running coroutine
By wrapping the loader with asyncio.shield, we can guarantee the wrapped function will only execute once and that it will not be interrupted even if the calling task is
Separately, what do you think about refactoring out already_loaded and not_loaded? I think it's appropriate since they are both suitable to be inlined and are not really useful as part of the public API. If you agree with this change I'll make a separate PR.