【Puerts】TypeScript实现C#风格的协程系统

发布于:2025-05-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

用TypeScript实现Unity风格的协程系统

前言

在Unity C#开发中,协程(Coroutine)是处理异步逻辑的神兵利器,但在TypeScript生态中,原生并不提供类似的协程机制。笔者在开发复杂前端动画逻辑时,面对层层嵌套的setTimeoutPromise.then(),决定借鉴C#协程设计思想,基于生成器函数和Promise封装了TS协程类。
借助Puerts的能力,可以在Unity或者UE的TS脚本中实现像C#一样的协程效果:通过yield return优雅地实现延时执行、等待任务(Promise、子协程)等异步操作,让定时任务、异步队列等场景的代码组织变得行云流水。

言归正传

1. 协程状态管理

通过枚举类型定义状态流转机制,核心状态机驱动协程生命周期,配合私有变量_state跟踪执行状态,通过isRunning和IsCompleted属性暴露运行状态:

enum ECoroutineState {
    Ready,      // 就绪状态
    Running,    // 执行中
    Paused,     // 暂停(当前未实现)
    Completed,  // 完成
    Failed      // 执行异常
}

private _state: ECoroutineState = ECoroutineState.Ready;

    /**
     * 协程是否完成
     */
    get IsCompleted(): boolean {
        return this._state === ECoroutineState.Completed;
    }

    /**
     * 协程是否正在运行
     */
    get isRunning(): boolean {
        return this._state === ECoroutineState.Running;
 }

2. 协程启动与停止

通过Start()方法启动协程并返回Promise,Stop()可强制终止执行:

Start(): Promise<T> {
    if (this._state !== ECoroutineState.Ready) return this._promise;
    this._state = ECoroutineState.Running;
    this.step();  // 开始迭代
    return this._promise;
}

Stop(): void {
    this._state = ECoroutineState.Completed;
}

3. 实现延时等待

WaitForSeconds将秒级等待封装为可yield的指令:

// 使用示例:yield WaitForSeconds(1.5)
export function WaitForSeconds(_Seconds: number): Coroutine<void> {
    return new Coroutine(function* () { 
        yield _Seconds * 1000; 
    });
}

在处理器中通过setTimeout实现延时:

// 内部处理逻辑
private handleDelay(_MS: number): void {
    setTimeout(() => this.step(), _MS);
}

4. 整合Promise支持

WaitForPromise包装现有Promise,实现协程内等待异步操作:

// 使用示例:yield WaitForPromise(fetch(url))
export function WaitForPromise<T>(_promise: Promise<T>): Coroutine<T> {
    return new Coroutine(function* () {
        return yield _promise;
    });
}

处理器通过then()连接Promise结果:

// Promise完成回调处理
private handlePromise(_promise: Promise<any>): void {
    _promise.then(
        (result) => this.step(result),
        (error) => this.step(error, true)
    );
}

5. 协程嵌套执行

通过WaitForCoroutine支持协程嵌套执行:

// 使用示例:yield WaitForCoroutine(subCoroutine)
export function WaitForCoroutine<T>(_coroutine: Coroutine<T>): Coroutine<T> {
    return new Coroutine(function* () {
        return yield _coroutine;
    });
}

子协程完成后通过Promise回调唤醒父协程:

// 子协程完成回调处理
private handleNestedCoroutine(_child: Coroutine<any>): void {
    _child.Start().then(
        (result) => this.step(result),
        (error) => this.step(error, true)
    );
}

放出代码

Coroutine.ts


/**
 * 协程状态枚举
 */
enum ECoroutineState {
    Ready,
    Running,
    Paused,
    Completed,
    Failed
}

/**
 * 协程类 - 模拟C#风格的协程 
 */
export class Coroutine<T = any> {
    private _generator: Generator<unknown, T, any>;
    private _iterator: IteratorResult<unknown, T>;
    private _state: ECoroutineState = ECoroutineState.Ready;
    private _resolve!: (_value: T) => void;
    private _reject!: (reason?: any) => void;
    private _promise: Promise<T>;
    private _childCoroutine: Coroutine<any> | null = null;

    /**
     * 创建协程
     * @param _generatorFn 生成器函数
     */
    constructor(_generatorFn: () => Generator<unknown, T, any>) {
        this._generator = _generatorFn();
        this._promise = new Promise((resolve, reject) => {
            this._resolve = resolve;
            this._reject = reject;
        });
    }

    /**
     * 启动协程
     */
    Start(): Promise<T> {
        if (this._state !== ECoroutineState.Ready) {
            return this._promise;
        }

        this._state = ECoroutineState.Running;
        this.step();
        return this._promise;
    }

    /**
     * 停止协程
     */
    Stop(): void {
        this._state = ECoroutineState.Completed;
    }

    /**
     * 协程是否完成
     */
    get IsCompleted(): boolean {
        return this._state === ECoroutineState.Completed;
    }

    /**
     * 协程是否正在运行
     */
    get isRunning(): boolean {
        return this._state === ECoroutineState.Running;
    }

    /**
     * 执行下一步
     */
    private step(_value?: any, _isError: boolean = false): void {
        if (this._state !== ECoroutineState.Running) {
            return;
        }

        try {
            if (_isError) {
                this._iterator = this._generator.throw(_value);
            } else {
                this._iterator = this._generator.next(_value);
            }

            if (this._iterator.done) {
                this._state = ECoroutineState.Completed;
                this._resolve(this._iterator.value);
                return;
            }

            this.handleYieldValue(this._iterator.value);
        } catch (error) {
            this._state = ECoroutineState.Failed;
            this._reject(error);
        }
    }

    /**
     * 处理yield返回的值
     * @param _value yield返回的值
     */
    private handleYieldValue(_value: unknown): void {
        if (_value instanceof Coroutine) {
            // 如果是嵌套协程
            this.handleNestedCoroutine(_value);
        } else if (_value instanceof Promise) {
            // 如果是Promise
            this.handlePromise(_value);
        } else if (typeof _value === 'number') {
            // 如果是数字,视为延时(毫秒)
            this.handleDelay(_value);
        } else {
            // 其他值直接继续
            setTimeout(() => this.step(_value), 0);
        }
    }

    /**
     * 处理嵌套协程
     * @param _child 子协程
     */
    private handleNestedCoroutine(_child: Coroutine<any>): void {
        this._childCoroutine = _child;
        _child.Start().then(
            (result) => {
                this._childCoroutine = null;
                this.step(result);
            },
            (error) => {
                this._childCoroutine = null;
                this.step(error, true);
            }
        );
    }

    /**
     * 处理Promise
     * @param _promise Promise对象
     */
    private handlePromise(_promise: Promise<any>): void {
        _promise.then(
            (result) => this.step(result),
            (error) => this.step(error, true)
        );
    }

    /**
     * 处理延时
     * @param _MS 毫秒数
     */
    private handleDelay(_MS: number): void {
        setTimeout(() => this.step(), _MS);
    }
}

/**
 * 辅助函数:创建延时协程
 * @param _MS 毫秒数
 * @returns 协程
 */
export function WaitForSeconds(_Seconds: number): Coroutine<void> {
    let ms=_Seconds*1000;
    return new Coroutine(function* () {        
        //let ms=_Seconds*1000;
        yield ms;
    });
}

/**
 * 辅助函数:等待Promise完成
 * @param _promise Promise对象
 * @returns 协程
 */
export function WaitForPromise<T>(_promise: Promise<T>): Coroutine<T> {
    return new Coroutine(function* () {
        return yield _promise;
    });
}

/**
 * 辅助函数:等待另一个协程完成
 * @param _coroutine 协程对象
 * @returns 协程
 */
export function WaitForCoroutine<T>(_coroutine: Coroutine<T>): Coroutine<T> {
    return new Coroutine(function* () {
        return yield _coroutine;
    });
}

调用例子

import {Coroutine,WaitForSeconds,WaitForPromise,WaitForCoroutine} from "./Coroutine";
import fetch from 'node-fetch';   //新增此行      
import * as JSON5 from 'json5';     

async function getRequest(url: string): Promise<void> {
  try {
    const response = await fetch(url);
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    console.log("getRequest",JSON5.stringify(data));
  } catch (error) {
    console.error('fetch error:', error);
  }
}

console.log('===Coroutine.Start()===>');

new Coroutine(function* () {

    //等待指定时间  
    yield WaitForSeconds(1);
    console.log('WaitForSeconds(1)');
    
    //等待http请求任务
    getRequest('http://t.weather.sojson.com/api/weather/city/101260804'); 

    //等待嵌套协程
    const result = yield WaitForCoroutine(
        new Coroutine(function* () {
            yield WaitForSeconds(1);
            return '嵌套结果'; 
        })
    );
    console.log('WaitForCoroutine嵌套协程结果:', result);
    return '所有操作完成';
}).Start();

总结

本文实现的协程系统具备以下优势:

多类型支持:可等待延时(秒)、可等待Promise、可等待子协程等不同对象

状态完备:完整生命周期的状态管理,支持异常捕获

链式调用:通过Promise链式调用实现异步流程控制

嵌套执行:支持无限层级协程嵌套,满足复杂业务需求

特别适合需要处理复杂时序逻辑的场景,如:游戏角色的连续动作、UI动画序列、分阶段加载流程等。相比原生Promise方案,代码可维护性显著提升,是Unity开发者转向TS开发时的平滑过渡方案。


网站公告

今日签到

点亮在社区的每一天
去签到