UniTask icon indicating copy to clipboard operation
UniTask copied to clipboard

UniTask convert to coroutine which can not be stopped

Open zrp2016 opened this issue 1 year ago • 1 comments

`async UniTask TestOnStart2() { int cnt = 0; while(true) { cnt++; Debug.LogError("11111111"); await UniTask.Yield(); Debug.LogError("111122222"); if (cnt >= 10) { Debug.LogError("stop cor"); Scheduler.Instance.StopCoroutine(cor); }

    }
   
}

IEnumerator cor; public async UniTaskVoid Start(Transform mainTransform) { var task2 = UniTask.Defer(async () => { await TestOnStart2(); }); cor = task2.ToCoroutine(); Debug.LogError("LazyStart"); Scheduler.Instance.StartCoroutine(cor); }`

run the code,and you will find that coroutine can not be stopped,how can I stop it?

zrp2016 avatar Jan 16 '24 07:01 zrp2016

Set the coroutine object to null after stopping it.

This test, based on your code, will succeed. However one extra iteration of the while loop will run after stopping the coutine.

Coroutine _coroutine;
int       _counter = 0;

[SetUp]
public void SetUp() {
    _coroutine = null;
    _counter = 0;
}

[UnityTest]
public IEnumerator UniTaskToCoroutineTest() => UniTask.ToCoroutine(async () => {
    _coroutine = CoroutineScheduler.Instance.StartCoroutine(AsyncCoroutineStopNull().ToCoroutine());
    await UniTask.WaitUntil(() => _counter >= 10);
    await UniTask.Delay(TimeSpan.FromSeconds(1));
    Assert.That(_counter, Is.EqualTo(10));
});

async UniTask AsyncCoroutineStopNull() {
    while (true) {
        _counter++;
        Debug.Log($"Tick {_counter}");
        await UniTask.Yield();
        if (_counter < 9) continue;
        Debug.Log("Stopping Coroutine");
        CoroutineScheduler.Instance.StopCoroutine(_coroutine);
        _coroutine = null;
    }
}

Where CoroutineScheduler is assumed to be this:

public class CoroutineScheduler : MonoBehaviour {
    static CoroutineScheduler _instance;
    public static CoroutineScheduler Instance => _instance ??= new GameObject(nameof(CoroutineScheduler)).AddComponent<CoroutineScheduler>();
}

Saismirk avatar Jan 18 '24 06:01 Saismirk

Set the coroutine object to null after stopping it.

This test, based on your code, will succeed. However one extra iteration of the while loop will run after stopping the coutine.

Coroutine _coroutine;
int       _counter = 0;

[SetUp]
public void SetUp() {
    _coroutine = null;
    _counter = 0;
}

[UnityTest]
public IEnumerator UniTaskToCoroutineTest() => UniTask.ToCoroutine(async () => {
    _coroutine = CoroutineScheduler.Instance.StartCoroutine(AsyncCoroutineStopNull().ToCoroutine());
    await UniTask.WaitUntil(() => _counter >= 10);
    await UniTask.Delay(TimeSpan.FromSeconds(1));
    Assert.That(_counter, Is.EqualTo(10));
});

async UniTask AsyncCoroutineStopNull() {
    while (true) {
        _counter++;
        Debug.Log($"Tick {_counter}");
        await UniTask.Yield();
        if (_counter < 9) continue;
        Debug.Log("Stopping Coroutine");
        CoroutineScheduler.Instance.StopCoroutine(_coroutine);
        _coroutine = null;
    }
}

Where CoroutineScheduler is assumed to be this:

public class CoroutineScheduler : MonoBehaviour {
    static CoroutineScheduler _instance;
    public static CoroutineScheduler Instance => _instance ??= new GameObject(nameof(CoroutineScheduler)).AddComponent<CoroutineScheduler>();
}

yeah,it does work,thanks a lot,but why is there one extra running happening?

zrp2016 avatar Jan 30 '24 15:01 zrp2016

Loops in a UniTask cannot be stopped from the outside with StopCoroutine.

If you want to stop a UniTask from the outside, use CancellationTokenSource.

    async UniTaskVoid Start()
    {
        var cts = new CancellationTokenSource();
        var task = TestOnStart2(cts.Token);

        // At any time of your choice.
        cts.Cancel(); // < -- cancel the task.
    }

    async UniTask TestOnStart2(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            Debug.Log("bra bra ..");
        }
    }

hadashiA avatar Feb 09 '24 06:02 hadashiA