/
crypt
/
NumbersPlay
Обзор
Документация
Войти
/
crypt
/
NumbersPlay
Код
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Assets/Plugins/UniTask/Runtime/UniTask.WhenEach.cs
183 строки
6 KB
Crypt
start
16 ноя 2025, 14:28
16 ноя 2025, 14:28
1482007
Код
Авторство
О чём код?
using Cysharp.Threading.Tasks.Internal; using System; using System.Collections.Generic; using System.Runtime.ExceptionServices; using System.Threading; namespace Cysharp.Threading.Tasks { public partial struct UniTask { public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(IEnumerable<UniTask<T>> tasks) { return new WhenEachEnumerable<T>(tasks); } public static IUniTaskAsyncEnumerable<WhenEachResult<T>> WhenEach<T>(params UniTask<T>[] tasks) { return new WhenEachEnumerable<T>(tasks); } } public readonly struct WhenEachResult<T> { public T Result { get; } public Exception Exception { get; } //[MemberNotNullWhen(false, nameof(Exception))] public bool IsCompletedSuccessfully => Exception == null; //[MemberNotNullWhen(true, nameof(Exception))] public bool IsFaulted => Exception != null; public WhenEachResult(T result) { this.Result = result; this.Exception = null; } public WhenEachResult(Exception exception) { if (exception == null) throw new ArgumentNullException(nameof(exception)); this.Result = default; this.Exception = exception; } public void TryThrow() { if (IsFaulted) { ExceptionDispatchInfo.Capture(Exception).Throw(); } } public T GetResult() { if (IsFaulted) { ExceptionDispatchInfo.Capture(Exception).Throw(); } return Result; } public override string ToString() { if (IsCompletedSuccessfully) { return Result?.ToString() ?? ""; } else { return $"Exception{{{Exception.Message}}}"; } } } internal enum WhenEachState : byte { NotRunning, Running, Completed } internal sealed class WhenEachEnumerable<T> : IUniTaskAsyncEnumerable<WhenEachResult<T>> { IEnumerable<UniTask<T>> source; public WhenEachEnumerable(IEnumerable<UniTask<T>> source) { this.source = source; } public IUniTaskAsyncEnumerator<WhenEachResult<T>> GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new Enumerator(source, cancellationToken); } sealed class Enumerator : IUniTaskAsyncEnumerator<WhenEachResult<T>> { readonly IEnumerable<UniTask<T>> source; CancellationToken cancellationToken; Channel<WhenEachResult<T>> channel; IUniTaskAsyncEnumerator<WhenEachResult<T>> channelEnumerator; int completeCount; WhenEachState state; public Enumerator(IEnumerable<UniTask<T>> source, CancellationToken cancellationToken) { this.source = source; this.cancellationToken = cancellationToken; } public WhenEachResult<T> Current => channelEnumerator.Current; public UniTask<bool> MoveNextAsync() { cancellationToken.ThrowIfCancellationRequested(); if (state == WhenEachState.NotRunning) { state = WhenEachState.Running; channel = Channel.CreateSingleConsumerUnbounded<WhenEachResult<T>>(); channelEnumerator = channel.Reader.ReadAllAsync().GetAsyncEnumerator(cancellationToken); if (source is UniTask<T>[] array) { ConsumeAll(this, array, array.Length); } else { using (var rentArray = ArrayPoolUtil.Materialize(source)) { ConsumeAll(this, rentArray.Array, rentArray.Length); } } } return channelEnumerator.MoveNextAsync(); } static void ConsumeAll(Enumerator self, UniTask<T>[] array, int length) { for (int i = 0; i < length; i++) { RunWhenEachTask(self, array[i], length).Forget(); } } static async UniTaskVoid RunWhenEachTask(Enumerator self, UniTask<T> task, int length) { try { var result = await task; self.channel.Writer.TryWrite(new WhenEachResult<T>(result)); } catch (Exception ex) { self.channel.Writer.TryWrite(new WhenEachResult<T>(ex)); } if (Interlocked.Increment(ref self.completeCount) == length) { self.state = WhenEachState.Completed; self.channel.Writer.TryComplete(); } } public async UniTask DisposeAsync() { if (channelEnumerator != null) { await channelEnumerator.DisposeAsync(); } if (state != WhenEachState.Completed) { state = WhenEachState.Completed; channel.Writer.TryComplete(new OperationCanceledException()); } } } } }