ValueTask<T> is a new standard of define async methods especially after being introduced IValueTaskSource. But it lacks utility like WhenAny, etc. ValueTaskSupplement appends supplemental methods(WhenAny, WhenAll, Lazy) to ValueTask and it is implemented by IValueTaskSource so fast and less allocation.
PM> Install-Package ValueTaskSupplement
usingValueTaskSupplement;// namespaceasyncValueTaskDemo(){// `ValueTaskEx` is the only types from provided this library// like this individual typesValueTask<int>task1=LoadAsyncA();ValueTask<string>task2=LoadAsyncB();ValueTask<bool>task3=LoadAsyncC();// await ValueTasks(has different type each other) with tuplevar(a,b,c)=awaitValueTaskEx.WhenAll(task1,task2,task3);// WhenAny with int winIndexvar(winIndex,a,b,c)=awaitValueTaskEx.WhenAny(task1,task2,task2);// like Timeoutvar(hasLeftResult,value)=awaitValueTaskEx.WhenAny(task1,Task.Delay(TimeSpan.FromSeconds(1)));if(!hasLeftResult)thrownewTimeoutException();// Lazy(called factory once and delayed)AsyncLazy<int>asyncLazy=ValueTaskEx.Lazy(async()=>9999);}// Same type and return array(same as Task.WhenAll).publicstaticValueTask<T[]>WhenAll<T>(IEnumerable<ValueTask<T>>tasks);// T0, T1, to...publicstaticValueTask<(T0,T1)>WhenAll<T0,T1>(ValueTask<T0>task0,ValueTask<T1>task1);
...// T0 ~ T15
public static ValueTask<(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15)>WhenAll<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15>(ValueTask<T0>task0,ValueTask<T1>task1,ValueTask<T2>task2,ValueTask<T3>task3,ValueTask<T4>task4,ValueTask<T5>task5,ValueTask<T6>task6,ValueTask<T7>task7,ValueTask<T8>task8,ValueTask<T9>task9,ValueTask<T10>task10,ValueTask<T11>task11,ValueTask<T12>task12,ValueTask<T13>task13,ValueTask<T14>task14,ValueTask<T15>task15);IEnumerable<ValueTask<T>> and (ValueTask<T0>, ValueTask<T1>, ...) can await directly.
usingValueTaskSupplement;// same as ValueTaskEx.WhenAll(new []{ }), ValueTaskEx.WhenAll(A, B, C)varresult=awaitnew[]{LoadAsyncA(),LoadAsyncB(),LoadAsyncC()};var(x,y,z)=await(LoadAsyncA(),LoadAsyncB(),LoadAsyncC());// binary api is useful to await with Delay(like for check Timeout).publicstaticValueTask<(boolhasResultLeft,Tresult)>WhenAny<T>(ValueTask<T>left,Taskright);publicstaticValueTask<(boolhasResultLeft,Tresult)>WhenAny<T>(ValueTask<T>left,ValueTaskright);// receive sequence is like Task.WhenAny but returns `int winArgumentIndex`.publicstaticValueTask<(intwinArgumentIndex,Tresult)>WhenAny<T>(IEnumerable<ValueTask<T>>tasks);// Return result of tuple methods is guaranteed only winArgumentIndex valuepublicstaticValueTask<(intwinArgumentIndex,T0result0,T1result1)>WhenAny<T0,T1>(ValueTask<T0>task0,ValueTask<T1>task1);
...// T0 ~ T15
public static ValueTask<(intwinArgumentIndex,T0result0,T1result1,T2result2,T3result3,T4result4,T5result5,T6result6,T7result7,T8result8,T9result9,T10result10,T11result11,T12result12,T13result13,T14result14,T15result15)>WhenAny<T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15>(ValueTask<T0>task0,ValueTask<T1>task1,ValueTask<T2>task2,ValueTask<T3>task3,ValueTask<T4>task4,ValueTask<T5>task5,ValueTask<T6>task6,ValueTask<T7>task7,ValueTask<T8>task8,ValueTask<T9>task9,ValueTask<T10>task10,ValueTask<T11>task11,ValueTask<T12>task12,ValueTask<T13>task13,ValueTask<T14>task14,ValueTask<T15>task15);// AsyncLazy<T> is similar to Lazy<T>, it can store in field// it await directly or can convert to ValueTask easily to use WhenAll.publicstaticAsyncLazy<T>Lazy<T>(Func<ValueTask<T>>factory);publicclassAsyncLazy<T>{publicValueTask<T>AsValueTask();publicValueTaskAwaiter<T>GetAwaiter();publicstaticimplicitoperatorValueTask<T>(AsyncLazy<T>source);}publicstaticValueTask<T>FromResult<T>(Tresult);publicstaticValueTaskFromTask(Taskresult);publicstaticValueTask<T>FromTask<T>(Task<T>result);publicstaticValueTaskAsValueTask(thisTaskresult);publicstaticValueTask<T>AsValueTask<T>(thisTask<T>result);This library is under the MIT License.