Generator函数在流程控制中的应用

扯蛋

作了两年的Nodejs全栈开发,不知道为何跑来作游戏呢(大概是厦门nodejs很差找工做吧)。用的是网易的pomelo的游戏框架。现接手了一个棋牌游戏:二十一点,不懂的规则的能够自行百度。
图片描述javascript

二十一点游戏流程图

图片描述

现状

接手了平台其余相关游戏的代码,流程控制相互交错,不易理解、难以维护。(多是刚作游戏的缘由,若是大家有什么更简单的流程控制方法,欢迎分享)。我下意识的就想到了Generator函数的特性,感受用着这里很是方便(之前一直以为这是个异步流程控制中过分性质的方法,而且须要配合co才能自动执行,因此基本没实际用过,koa不算...)java

Js Generator函数实现流程控制的优势

一、易于理解、便于开发、容易维护(看到Generator函数犹如看到了流程图)
二、开发思路清晰(每一个阶段(函数)只须要关注本身的业务逻辑,完成直接下一步,而不用管下一步要作什么操做)
三、不存在会忘记清除定时器的问题node

简单说下Generator的执行流程

一、Generator函数执行后会生成一个Iterator。(注意不要加new)(简单说就是个有next方法的对象,执行一次返回一个值)
二、每次next的调用,执行yield后面的语句并返回该语句执行的结果
三、每次只执行一个yield,后面的语句不会再执行、只有在执行下次next函数时才执行。(能够利用这点作定时器的清理工做,并且能够说基本不会忘记)
四、yield* 能够将后面的变量(可迭代的变量,字符串、数组等)中的值一个一个的返回。执行一次返回其中的一个值数组

Js Generator函数完美实现流程图代码(部分)

class EsydProcess {

    constructor(room) {
        /**
         * ...其余变量
         */
        this.flow = this.flowGenerator();
    }

    *['flowGenerator']() {

        yield this.betStage(); // 下注
        this.betStageTimer && clearTimeout(this.betStageTimer);// 下注阶段完成后直接清除定时器。彻底不用担忧定时器没有被清理的状况

        yield this.assignStage(); // 分牌

        if (this.esydCard.getCardPoint(this.bankerCards[0]) === 1) {

            yield this.ensureStage(); // 保险
            this.ensureStageTimer && clearTimeout(this.ensureStageTimer);

            if (this.esydCard.getCardType(this.bankerCards) !== CardTypes.BLACK_JACK) {
                yield* this.operateStage(); // 操做
            }

        } else {
            yield* this.operateStage(); // 操做
        }

        yield this.settleStage(); // 结算
    }

    *['operateStage']() {
        // 通知进入玩家操做阶段
        this.noticeChangeStage(esydConsts.gameStage.OPERATE_STAGE);
        let players = this.players;
        for (let uid in players) {
            let player = players[uid];

            // 操做第一副牌
            yield this.changeOperatingPlayer(player);
            player.getCurCardInfo().isStop = true;
            this.operateTimer && clearTimeout(this.operateTimer);

            if (player.isSperated) {
                // 若是有一副牌,操做第二副牌
                player.curCardsIndex = 1;
                yield this.changeOperatingPlayer(player);
                player.getCurCardInfo().isStop = true;
                this.operateTimer && clearTimeout(this.operateTimer);
            }
        }
        yield this.bankerOperate(); // 庄家操做
    }

    // 转到下个阶段
    nextStage() {
        process.nextTick(() => {
            this.flow.next();
        });
    }
    
    // 开始游戏
    start(seats) {
        /**
         * ...
         *
         */
        // 第一次next,直接进入下注阶段。整个流程走完游戏结束
        this.nextStage();
    }
    
    betStage(){
        // 进入下注阶段
        this.noticeChangeStage(esydConsts.gameStage.BET_STAGE);
        /**
         * 其余操做
         */
        this.betStageTimer = setTimeout(() => {
            this.betStageTimer = null;
            // 超时,直接进入下一步。没有下注的玩家使用默认底注
            this.nextStage();
        }, esydConsts.stageTime.BET_STAGE);
    }

    assignStage(){
        /**
         * ...
         * 分牌操做,完成直接下一步
         */
        this.nextStage();
    }

    ensureStage(){
        /**
         * ...
         * 各类操做,如通知客户端、开始超时定时器等。操做完后直接下一步就Ok了。只须要专一当前函数的功能,完成直接下一步
         */
        this.nextStage();
    }

    // 监听用户下注操做
    userBetOperateListener(uid){
    
        /**
         * ...
         * 检查是否在下注阶段,不是不能下注、记录每一个玩家下注等
         */
         
        // 记录已下注的玩家
        this.betedPlayers[uid] = true;
        if (Object.keys(this.betedPlayers).length === this.seatCount) {
            // 若是全部玩家都押注完毕,
            this.nextStage();
        }
    }
    
    /**
     *  其余函数...
     */

}
相关文章
相关标签/搜索