zoukankan      html  css  js  c++  java
  • 回调在事件中的妙用

    回调定义

    CallBack: A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

    回调: 回头调用,函数 A 的事先干完,回头再调用函数 B。


    函数 A 的参数为函数 B, 函数 B 被称为回调函数。至于为何要用参数的形式传入,而不是直接在 A 中直接调用 B 函数,主要是为了变量的灵活性考虑。
    为何要使用回调?

    • 比较常见的情况是两个不同模块之间需要相互调用
    • 事件中的使用。

    详细说一下最近使用一个事件的时候遇到的问题,当时琢磨了半天没有想到解决方案,最后同事一句话点醒我,为毛不用回调,问题解决了。

    需求如下:

    创建一个标注,同时具体 撤销恢复 功能, 具体介绍如下:

    • 想实现一个 cad 里面的创建标注的功能, 用户点击创建标注按钮 --> 点击绘图界面,创建一条跟随鼠标移动的直线 --> 再次绘图界面,创建跟随鼠标拖动标注 -->
      第三次点击绘图界面,确定标注位置。在操作过程中,按 Esc 键,可取消创建。创建的标注可以 撤销 与 恢复,也就是 Undo & Redo 。

    • 命令模式,具体创建标注的类如下:

      export class DimAddCmd implements ICommand {
            undoDimSpritePairs: Stack<DimSpritePair>;
            redoDimSpritePairs: Stack<DimSpritePair>;
            constructor() {
                this.undoDimSpritePairs = new Stack<DimSpritePair>();
                this.redoDimSpritePairs = new Stack<DimSpritePair>();
            }
    
            /**
             * 执行创建命令
             * @param {Function} success
             * @returns {boolean} 
             * @memberof DimAddCmd
             */
            Execute(): boolean {
                let dimEvent = new DimEvent();
                let flag = false;
                dimEvent.SelectDimButton();
                Laya.stage.on(Laya.Event.MOUSE_MOVE, dimEvent, dimEvent.OnShowClickPoint);
                Laya.stage.on(Laya.Event.MOUSE_DOWN, dimEvent, dimEvent.OnDrawDim, [this.undoDimSpritePairs, flag]);
                Laya.stage.on(Laya.Event.KEY_DOWN, dimEvent, dimEvent.DimKeyInfo);
                return flag;
            }
    
            Undo(): void {
                let currentPair = this.undoDimSpritePairs.Pop();
                if (currentPair)  {
                    this.redoDimSpritePairs.Push(currentPair);
                    currentPair.dimSp.removeSelf();
                }
                else  {
                    console.log("add dim undo stack is empty!")
                }
            }
    
            Redo(): void {
                let currentPair = this.redoDimSpritePairs.Pop();
                if (currentPair)  {
                    this.undoDimSpritePairs.Push(currentPair);
                    currentPair.dimParentSp.addChild(currentPair.dimSp);
                }
                else  {
                    console.log("add_dim redo stack is empty!")
                }
            }
        }
    

    • 命令模式中的 control 类:
    module Command {
        export class Control {
            onCommands: ICommand[]
            undoCommands: Stack<ICommand>;
            redoCommands: Stack<ICommand>;
            private _cmdNum: number = 7;
    
            constructor()  {
                this.onCommands = new Array<ICommand>(this._cmdNum);
                this.undoCommands = new Stack<ICommand>();
                this.redoCommands = new Stack<ICommand>();
    
                // TODO: 定义枚举,实现按键与命令的绑定
                for (let i = 0; i < this._cmdNum; i++)  {
                    this.onCommands[i] = null;  // TODO: create NoCommand class
                }
            }
    
            public SetCommand(cmd: ICommand, cmdType: CommandType): void {
                this.onCommands[cmdType] = cmd;
            }
    
            public OnButttonWasPressed(cmdType: CommandType): void  {
                let cmd = this.onCommands[cmdType];
                if (cmd)  {
                    let isSuccess = cmd.Execute();
                    if(isSuccess)
                    {
                        this.undoCommands.Push(cmd);
                    }
                }
            }
    
            public Undo(): void {
                if (this.undoCommands.Count()) {
                    let cmd = this.undoCommands.Pop();
                    this.redoCommands.Push(cmd);
                    cmd.Undo();
                }
                else {
                    console.log("undo stack is empty!");
                }
            }
    
            public Redo(): void {
                if (this.redoCommands.Count()) {
                    let cmd = this.redoCommands.Pop();
                    this.undoCommands.Push(cmd);
                    cmd.Redo();
                }
                else  {
                    console.log("redo stack is empty!");
                }
            }
        }
    }
    

    其中涉及到两个函数的调用问题:

    Control.ts:
        public OnButttonWasPressed(cmdType: CommandType): void  {
            let cmd = this.onCommands[cmdType];
            if (cmd)  {
                let isSuccess = cmd.Execute();
                if(isSuccess)
                {
                    this.undoCommands.Push(cmd);
                }
            }
        }
    
    DimAddCmd.ts:
        public Execute(): boolean {
                let dimEvent = new DimEvent();
                dimEvent.SelectDimButton();
                let flag = false;
                Laya.stage.on(Laya.Event.MOUSE_MOVE, dimEvent, dimEvent.OnShowClickPoint);
                Laya.stage.on(Laya.Event.MOUSE_DOWN, dimEvent, dimEvent.OnDrawDim, [this.undoDimSpritePairs, flag]);
                Laya.stage.on(Laya.Event.KEY_DOWN, dimEvent, dimEvent.DimKeyInfo);
                return flag;
            }
    

    以上代码块中,按键后,需要判断是否成功创建标注,若成功创建标注,则将 dimAddCmd 加入到 undoCommands 栈中。 从逻辑上来说,这样是没有问题的。那么问题在哪?

    Execute() 方法中,创建标注的方法绑定在事件中,事件的触发是在另一个线程中执行, 因为 Mouse_Down 事件在我们点击画布之前,无法触发,所以 flag 的值永远都是 false。因此, undo 栈中永远无法添加绘制标注命令。

    如果将 “命令 push 到栈中” 的操作放在事件函数里面来操作,是不是问题就解决了?

    是滴,这样可以解决问题。但是要将 pushbutton 方法静态化,不是特别方便。

    这个时候,使用回调的概念,将函数当参数传入,问题轻松加愉快的就解决了。
    具体代码如下:

    Control.ts:
        // 创建一个匿名方法,在这个方法里面完成 “push cmd 到 undo 栈”, 并将这个方法座位 Execute() 方法的参数
        public OnButttonWasPressed(cmdType: CommandType): void  {
            let cmd = this.onCommands[cmdType];
            if (cmd)  {
                // 注意此处改变
                cmd.Execute(()=>{ this.undoCommands.Push(cmd);});  // 多个参数的统一
            }
        }
    
    DimAddCmd.ts:
        public Execute(success: Function): boolean {
                let dimEvent = new DimEvent();
                dimEvent.SelectDimButton();
                Laya.stage.on(Laya.Event.MOUSE_MOVE, dimEvent, dimEvent.OnShowClickPoint);
                // 注意方法中传入了一个函数
                Laya.stage.on(Laya.Event.MOUSE_DOWN, dimEvent, dimEvent.OnDrawDim, [this.undoDimSpritePairs, success]);
                Laya.stage.on(Laya.Event.KEY_DOWN, dimEvent, dimEvent.DimKeyInfo);
            }
    

    这样,点击绘图面版,触发标注创建后, 在 onDrawDim 方法里面,执行一次 success 函数,即可将 cmd 添加到 undo 栈中去。

    通过以上方式,可以中其本模块中调用其它模块变量,有些细节与严格意义上回调不一致,但基本思想一致。

  • 相关阅读:
    abp架构中加载公共css样式表和公共js的文件目录位置
    angular中[hidden]="expression"注意事项
    angular中使用canvas画布做验证码
    AngularJs页面跳转
    Angular学习笔记【如何正确使用第三方组件】
    【JavaScript权威指南】——逻辑与(&&)
    angular学习笔记【ng2-charts】插件添加
    OpenLayers v4.2.0 -----地图延迟加载;
    Sharepoint 图片库字段名称(Title)和对应的内部名称(InternalName)
    Sharepoint JSCOM 列表操作
  • 原文地址:https://www.cnblogs.com/yaolin1228/p/10656442.html
Copyright © 2011-2022 走看看