UniTask使用笔记,将Action回调改写成await,以PlayableDirector的Stopped为例

UniTask提供了很多异步方法,如Resources.LoadAsync,UniTask.NextFrame,UniTask.WaitUntil,按钮事件button.OnClickAsync等
当没有内置方法时,我们希望能将自己的回调方法改写为await的形式。
比如将PlayableDirector的stopped回调改写成await,实现如下代码。

private async UniTask PlayAndDebug()
{
    await _director.OnStoppedAsync();
    Debug.Log("director stopped,do something else");
}

原理:通过实现IUniTaskSource和IUniTaskSource接口,实现自己的await逻辑,实现接口我们可以参考AsyncUnityEventHandler里的方法


public class PlayableDirectorAsyncHandler:IUniTaskSource
    {
        static Action<object> cancellationCallback = CancellationCallback;
        
        private CancellationToken cancellationToken;
        private CancellationTokenRegistration registration;
        
        private UniTaskCompletionSourceCore<bool> core;
        bool isDisposed;
        bool callOnce;

        private PlayableDirector _director;

        public PlayableDirectorAsyncHandler(PlayableDirector director,CancellationToken cancellationToken, bool callOnce)
        {
            this.cancellationToken = cancellationToken;
            if (cancellationToken.IsCancellationRequested)
            {
                isDisposed = true;
                return;
            }
            this.callOnce = callOnce;
            _director = director;
            _director.stopped += OnDirectorStopped;
            
            if (cancellationToken.CanBeCanceled)
            {
                registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
            }

            TaskTracker.TrackActiveTask(this, 3);
        }
        
        static void CancellationCallback(object state)
        {
            var self = (PlayableDirectorAsyncHandler)state;
            self.Dispose();
        }

        private void OnDirectorStopped(PlayableDirector obj)
        {
            core.TrySetResult(true);
        }
        
        public void Dispose()
        {
            if (!isDisposed)
            {
                isDisposed = true;
                TaskTracker.RemoveTracking(this);
                registration.Dispose();
                _director.stopped -= OnDirectorStopped;
                core.TrySetCanceled(cancellationToken);
            }
        }
        
        public UniTask OnInvokeAsync()
        {
            core.Reset();
            if (isDisposed)
            {
                core.TrySetCanceled(this.cancellationToken);
            }
            return new UniTask(this, core.Version);
        }

        public UniTaskStatus GetStatus(short token)
        {
            return core.GetStatus(token);
        }

        public void OnCompleted(Action<object> continuation, object state, short token)
        {
            core.OnCompleted(continuation, state, token);
        }

        public void GetResult(short token)
        {
            try
            {
                core.GetResult(token);
            }
            finally
            {
                if (callOnce)
                {
                    Dispose();
                }
            }
        }

        public UniTaskStatus UnsafeGetStatus()
        {
            return core.UnsafeGetStatus();
        }
    }

最后实现一个扩展方法

public static class UniTaskAsyncExtensions
{
    public static UniTask OnStoppedAsync(this PlayableDirector director,CancellationToken cancellationToken = default,bool callOnce = true)
    {
        return new PlayableDirectorAsyncHandler(director,cancellationToken, callOnce).OnInvokeAsync();
    }
}

扩展完成。

posted @ 2022-10-22 16:19  jeoyao  阅读(813)  评论(0编辑  收藏  举报