UniRx_UniTask_学习记录_3.6_Coroutine的变换

3.6 Coroutine变换

Coroutine 是Unity标准的异步处理,UniRx可以把Coroutine 转换成Observable

3.6.1 Coroutine => Observable

等待 Coroutine 执行完

Observable.FromCoroutine 把Coroutine 变成Observable,生成的Observable在对象Coroutine结束时发送消息

Observable.FromCoroutine把Coroutine 和Observable 绑定在一起,当Subscribe() 被Dispose()时,执行中的Coroutine 会自动停止

// 不使用 CancellationToken 的时候
public static IObservable<Unit> FromCoroutine(
    Func<IEnumerator> coroutine,
    bool publishEveryYield = false)
    
// 使用 CancellationToken 的时候
public static IObservable<Unit> FromCoroutine(
    Func<CancellationToken, IEnumerator> coroutine,
    bool publishEveryYield = false)

使用例 等待Coroutine 执行完了

using System;
using System.Collections;
using UniRx;
using UnityEngine;

namespace Sample.Section3.Coroutines
{
    public class FromCoroutineSample1 : MonoBehaviour
    {
        private void Start()
        {
            // Coroutine 结束时用 Observable 接受消息
            Observable.FromCoroutine(WaitingCoroutine, publishEveryYield: false)
                .Subscribe(
                    _ => Debug.Log("OnNext"),
                    () => Debug.Log("OnCompleted"))
                .AddTo(this);
            // ToObservable()  简单写法 糖语法
            // WaitingCoroutine().ToObservable()
            // .Subscribe();
        }
    
        private IEnumerator WaitingCoroutine()
        {
            Debug.Log("Coroutine start.");
            // 等待3s
            yield return new WaitForSeconds(3);
            Debug.Log("Coroutine finish.");
        }
    }
}

使用例 使用 CancellationToken

CancellationToken 可以把由FromCoroutine 生成的Observable 订阅中断取消,同时也停止这个协程

using System;
using System.Collections;
using System.Threading;
using UniRx;
using UnityEngine;

namespace Sample.Section3.Coroutines
{
    public class FromCoroutineSample2 : MonoBehaviour
    {
        private void Start()
        {
            //  使用 CancellationToken 
            Observable
                .FromCoroutine(token => WaitingCoroutine(token))
                .Subscribe(
                    _ => Debug.Log("OnNext"),
                    () => Debug.Log("OnCompleted"))
                .AddTo(this);
        }
        // CancellationToken 作为参数
        private IEnumerator WaitingCoroutine(CancellationToken token)
        {
            Debug.Log("Coroutine start.");
            // Observable 在等待Coroutine 时
            // 当这个Coroutine 停止时
            // yield return 让等待着的Observable 也停下来
            // 所以 使用 CancellationToken
            yield return Observable
                .Timer(TimeSpan.FromSeconds(3))
                
                .ToYieldInstruction(token); // 因为Observable 停下来了 到这会停止Coroutine
            
            Debug.Log("Coroutine finish.");
        }
    }
}

** Coroutine 生成任意的Observable **

Observable.FromCoroutine 可以生成 发送任意消息的Observable

通过委托 将消息传递出去

public static IObservable<T> FromCoroutine<T>(
Func<IObserver<T>, IEnumerator> coroutine)
    
public static IObservable<T> FromCoroutine<T>(
Func<IObserver<T>, CancellationToken, IEnumerator> coroutine)
    

**使用例 **

using System;
using System.Collections;
using UniRx;
using UnityEngine;

namespace Sample.Section3.Coroutines
{
    public class FromCoroutineSample3 : MonoBehaviour
    {
        //长按判定时间
        private readonly float _longPressThresholdSeconds = 1.0f;

        private void Start()
        {
            // 检测一定时间长按的Observable
            Observable.FromCoroutine<bool>(observer => LongPushCoroutine(observer))
                .DistinctUntilChanged() // 去除重复的消息
                .Subscribe(x => Debug.Log(x)).AddTo(this);
        }
        
        // 检测空格键被长按
        // 经过一定时间返回true
        // 放开按键 返回false
        private IEnumerator LongPushCoroutine(IObserver<bool> observer)
        {
            var isPushed = false;
            var lastPushTime = Time.time;
            while (true)
            {
                if (Input.GetKey(KeyCode.Space))
                {
                    if (!isPushed)
                    {
                        // 按下的时间
                        lastPushTime = Time.time;
                        isPushed = true;
                    }
                    else if (Time.time - lastPushTime > _longPressThresholdSeconds)
                    {
                        // 按下时发送true
                        observer.OnNext(true);
                    }
                }
                else
                {
                    if (isPushed)
                    {
                        // 抬起时发送false
                        observer.OnNext(false);
                        isPushed = false;
                    }
                }

                yield return null;
            }
        }
    }
}

** 只是用 yield return null 的Coroutine 的最优化**

Observable.FromCoroutine 的派生、性能更好的Observable.FromMicroCoroutine,这里内部只使用了yield return null

比 Observable.FromCoroutine 相对轻量

使用方法和 Observable.FromCoroutine一样 ,根据状况不同,区别使用

// 等待协程结束
public static IObservable<Unit> FromMicroCoroutine(
    Func<IEnumerator> coroutine,
    bool publishEveryYield = false,
    FrameCountType frameCountType = FrameCountType.Update)
    
// 等待协程结束 使用 CancellationToken
public static IObservable<Unit> FromMicroCoroutine(
    Func<CancellationToken, IEnumerator> coroutine,
    bool publishEveryYield = false,
    FrameCountType frameCountType = FrameCountType.Update)
    
// 协程生成 任意的 Observable
public static IObservable<T> FromMicroCoroutine<T>(
    Func<IObserver<T>, IEnumerator> coroutine,
    FrameCountType frameCountType = FrameCountType.Update)
    
// 协程生成 任意的 Observable 使用 CancellationToken
public static IObservable<T> FromMicroCoroutine<T>(
    Func<IObserver<T>, CancellationToken, IEnumerator> coroutine,
    FrameCountType frameCountType = FrameCountType.Update)

**使用例 **

using System.Collections;
using UniRx;
using UnityEngine;

namespace Sample.Section3.Coroutines
{
    public class FromMicroCoroutineSample : MonoBehaviour
    {
        private void Start()
        {
            // FromCoroutine 
            Observable
                .FromCoroutine(() => WaitingCoroutine(5))
                .Subscribe();
            // 对象协程只使用了 yield return null
            // 可以使用 FromMicroCoroutine  
            Observable
                .FromMicroCoroutine(() => WaitingCoroutine(5))
                .Subscribe();
        }
        // 等待指定秒数
        private IEnumerator WaitingCoroutine(float seconds)
        {

            var start = Time.time;
            while (Time.time - start <= seconds)
            {
                yield return null;
            }
        }
    }
}

用 yield return 发送消息

Observable.FromCoroutineValue 可以把 yield return 的值 作为OnNext 消息取出

// 不使用 CancellationToken 
public static IObservable<T> FromCoroutineValue<T>(
Func<IEnumerator> coroutine,
bool nullAsNextUpdate = true)
// 使用 CancellationToken 
public static IObservable<T> FromCoroutineValue<T>(
Func<CancellationToken, IEnumerator> coroutine,
bool nullAsNextUpdate = true)

3.6.2 Observable => Coroutine

反过来 Observable 通过 YieldInstruction 让一个Coroutine 来接受

ToYieldInstruction

通过 ToYieldInstruction ,让Observable 变换成CustomYieldInstruction

可以实现 await/ async 相似的举动

使用例

using System;
using System.Collections;
using UniRx;
using UnityEngine;

namespace Sample.Section3.Coroutines
{
    public class YieldInstructionSample1 : MonoBehaviour
    {
        private void Start()
        {
            StartCoroutine(WaitCoroutine());
        }
        private IEnumerator WaitCoroutine()
        {
            Debug.Log("Coroutine start:" + Time.time);
            // Observable => Coroutine
            yield return Observable
                .Timer(TimeSpan.FromSeconds(1))
                .ToYieldInstruction();
            Debug.Log("Coroutine end:" + Time.time);
        }
    }
}

等待一个无限长的 Observable

通过ToYieldInstruction() 变换的Observable 在发送OnCompleted消息之前都是处于未完成状态,所以当Observable 会无限长的情况下需要使用Take(1) 之类的 Operator来发送OnCompleted消息

使用例

using System.Collections;
using UniRx;
using UnityEngine;
using UnityEngine.UI;

namespace Sample.Section3.Coroutines
{
    public class YieldInstructionSample2 : MonoBehaviour
    {
        [SerializeField] private Button _moveButton;
        private void Start()
        {
            StartCoroutine(MoveCoroutine());
        }
        // 按下 Button 在1秒间 Object 会前进
        private IEnumerator MoveCoroutine()
        {
            while (true)
            {
                // 等待 Button 按下
                // OnClickAsObservable()是无限长的Stream、使用Take(1) 执行1次
                yield return _moveButton
                    .OnClickAsObservable()
                    .Take(1)
                    .ToYieldInstruction();
                var start = Time.time;
                while (Time.time - start <= 1.0f)
                {
                    //前进
                    transform.position += Vector3.forward * Time.deltaTime;
                    yield return null;
                }
            }
        }
    }
}

取出 Observable 的结果

ToYieldInstruction() 可以作为变数,Observable 的值 在其的Result 属性里

使用例

using System;
using System.Collections;
using System.IO;
using UniRx;
using UnityEngine;

namespace Sample.Section3.Coroutines
{
    public class YieldInstructionSample3 : MonoBehaviour
    {
        private void Start()
        {
            StartCoroutine(ReadFileCoroutine());
        }
        // 异步读取FILE
        private IEnumerator ReadFileCoroutine()
        {
            //  Observable 进行 YieldInstruction 变换
            // throwOnError 为 false 时
            // 它将保存失败时的异常。
            // (如果为真,就会按原样抛出异常)
            var yi = ReadFileAsync(@"data.txt")
                .ToYieldInstruction(throwOnError: false);
            // 等待
            yield return yi;
            if (yi.HasError) // HasError 是否出错
            {
                // 失败时 保存异常信息
                Debug.LogError(yi.Error);
            }
            else
            {
                // 成功是输出 Result
                Debug.Log(yi.Result);
            }
 
        }
        // 生成 异步读取FIle 的IObservable
        private IObservable<string> ReadFileAsync(string path)
        {
            return Observable.Start(() =>
            {
                using (var r = new StreamReader(path))
                {
                    return r.ReadToEnd();
                }
            }, Scheduler.ThreadPool);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值