← Назад к вопросам

Как сделать так чтобы UniTask вела себя как Coroutine?

2.3 Middle🔥 121 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

UniTask как альтернатива Coroutine: подходы и практика

UniTask — это современная библиотека для асинхронного программирования в Unity, предлагающая более эффективную и гибкую альтернативу традиционным Coroutine. Хотя они имеют разные реализации, можно добиться схожего поведения, используя несколько ключевых подходов.

Основные сходства и различия

UniTask работает на основе async/await паттерна C#, тогда как Coroutine использует итераторы (IEnumerator). UniTask обеспечивает нулевые аллокации в runtime, лучшую производительность и интеграцию с CancellationToken для управления жизненным циклом.

Эмуляция поведения Coroutine

Чтобы UniTask вела себя подобно Coroutine, нужно имитировать два основных аспекта: плавное выполнение по кадрам и возможность остановки.

1. Использование UniTask.Delay для поэтапного выполнения

Coroutine часто используют yield return new WaitForSeconds() для задержек. В UniTask для этого используется UniTask.Delay, который можно ожидать с await.

using Cysharp.Threading.Tasks;
using UnityEngine;

public class UniTaskExample : MonoBehaviour
{
    async UniTaskVoid Start()
    {
        // Эмулируем Coroutine, которая выполняется 5 секунд с шагом в 1 секунду
        for (int i = 0; i < 5; i++)
        {
            Debug.Log($"Шаг: {i}");
            await UniTask.Delay(1000); // Задержка в миллисекундах
        }
        Debug.Log("Завершено");
    }
}

Для более точной эмуляции WaitForSeconds используйте UniTask.Delay(TimeSpan.FromSeconds(1)) или конфигурируйте задержку с помощью ignoreTimeScale.

2. Ожидание конца кадра и другие Yield Instructions

Coroutine использует yield return null или yield return new WaitForEndOfFrame(). В UniTask для этого есть специальные методы:

async UniTaskVoid UpdateLikeCoroutine()
{
    while (true)
    {
        // Эквивалент yield return null в Update
        await UniTask.Yield();
        
        // Ваша логика здесь
        transform.Translate(Vector3.forward * Time.deltaTime);
    }
}

Для других сценариев:

  • WaitForEndOfFrame: await UniTask.WaitForEndOfFrame(this.GetCancellationTokenOnDestroy());
  • WaitForFixedUpdate: await UniTask.WaitForFixedUpdate();
  • WaitUntil/WaitWhile: используйте await UniTask.WaitUntil(() => condition) или комбинируйте с циклом.

3. Управление жизненным циклом через CancellationToken

Одно из слабых мест Coroutine — сложность принудительной остановки. В UniTask это решается через CancellationToken.

using Cysharp.Threading.Tasks;
using System.Threading;
using UnityEngine;

public class StoppableTask : MonoBehaviour
{
    private CancellationTokenSource _cancellationTokenSource;

    void Start()
    {
        _cancellationTokenSource = new CancellationTokenSource();
        RunTask().Forget();
    }

    async UniTaskVoid RunTask()
    {
        try
        {
            while (true)
            {
                await UniTask.Delay(1000, cancellationToken: _cancellationTokenSource.Token);
                Debug.Log("Tick");
            }
        }
        catch (OperationCanceledException)
        {
            Debug.Log("Задача отменена");
        }
    }

    void OnDestroy()
    {
        _cancellationTokenSource?.Cancel();
        _cancellationTokenSource?.Dispose();
    }
}

4. Запуск и остановка как у Coroutine

Для запуска UniTask из MonoBehaviour просто вызовите асинхронный метод с суффиксом Forget() или используйте UniTask.Void. Для остановки — отменяйте токен.

// Запуск
private void Start()
{
    MyCoroutineLikeTask().Forget();
}

// Остановка через токен
private CancellationTokenSource _cts;
public void StopMyTask()
{
    _cts?.Cancel();
}

Практические рекомендации

  • Для новых проектов предпочтительно использовать UniTask из-за производительности и лучшего контроля.
  • При миграции с Coroutine заменяйте yield return на соответствующие await UniTask.* методы.
  • Используйте UniTask.RunOnThreadPool для CPU-нагрузочных операций, что невозможно в Coroutine.
  • Избегайте смешивания Coroutine и UniTask в одной цепочке операций без необходимости.

UniTask не просто эмулирует Coroutine, а предлагает более мощный инструментарий для асинхронных операций в Unity, сохраняя при этом привычные паттерны работы.

Как сделать так чтобы UniTask вела себя как Coroutine? | PrepBro