• lua函数回调技巧


    前言

    在使用lua 的开发中,有很多异步调用的场景存在,当某个场景中存在多个异步回调操作且该系列操作中每个操作必须依赖上一个操作的完成,这就形成了回调地狱,示例代码:

    function f()
        f1(function ()
            f2(function ()
                f3(function ()
                    --coding
                end)
            end)
        end)
    end
    

    优雅回调

    可以想象一个不需要层层嵌套的方式,比如参考js的async.js,而是像瀑布一样,一个个函数依次调用,示例代码:

    waterfall({
        function (cb)
            f(cb)
        end,
        function cb)
            f1(cb)
        end,
        function (cb)
            f2(cb)
        end,
        function (cb)
            f3(cb)
        end
    }, function ()
        -- coding
    end)
    

    要实现以上的效果,需要定义一个内部函数以及一个参数(回调函数)去调用第一个异步函数,当异步函数执行完成以后调用该回调函数,该回调函数内部继续调用下一个异步函数,当所有异步函数都执行完成以后调用最终的回调完成整个过程,这里需要定义一个规范,比如新函数第一个参数为error,如果错误了则终端整个执行过程,实现代码:

    function waterfall(tasks, cb)
        local index = 1
        local doNext
        local nextTask = function (...)
            local args = {...}
            local count = select('#', ...)
            args[count + 1] = function (...)
                doNext(...)
            end
            tasks[index](
                table.unpack(args)
            )
        end
        doNext = function (err, ...)
            index = index + 1
            if err or index > #tasks then
                return cb(err, ...)
            end
    
            nextTask(...)
        end
    
        nextTask()
    end
    

    协程

    回调的方式的确有点丑陋,代码量也有点多,如果能像普通调用代码那样,让函数一个个执行,还能达到异步回调的效果,示例代码:

        f()
        f1()
        f2()
        f3()
    

    然而这个效果在当前的lua中是无法实现的,但是合理利用协程的话还是可以接近这个效果的,实现代码:

    local function await(fn)
        local run = coroutine.running()
        fn(function()
            coroutine.resume(run)
        end)
        coroutine.yield()
    end
    
    function f()
        local co = coroutine.create(function()
            await(f1)
            await(f2)
            await(f3)
        end)
        coroutine.resume(co)
    end
    

    用协程去执行第一个异步函数,同时跳回主程序,因为协程跟主程序在同一线程,因此,在协程里调用跟在主程序调用是一样的,当异步调用完成再跳回协程,继续下一个异步调用,如此循环.

    结束语

    解决函数回调地狱的方式有很多种,比如现在比较流行的Promise保持代码简短模块化等等.虽然协程封装会在一定程度上会增加性能的损耗,但是能更直观的表达代码的业务逻辑,简化开发维护的成本,始终保持代码简洁才是最重要的.

  • 相关阅读:
    数据库设计 概念结构设计(以权限系统为例)
    sp_addlinkedserver使用方法
    动态页面静态化技术(很不错的教程摘录)
    C#获取硬件序列号
    [转]消息队列关于MSMQ的基础知识
    通过Image对象获取对象的格式
    历史项目的回忆 2008.04.27
    解决Access to Message Queuing system is denied.权限问题
    windows 输入法生成器 导出输入法的mb文件到txt
    [转载]C#版可调节的文字阴影特效
  • 原文地址:https://www.cnblogs.com/ahl5esoft/p/12992059.html
Copyright © 2020-2023  润新知