zoukankan      html  css  js  c++  java
  • 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保持代码简短模块化等等.虽然协程封装会在一定程度上会增加性能的损耗,但是能更直观的表达代码的业务逻辑,简化开发维护的成本,始终保持代码简洁才是最重要的.

  • 相关阅读:
    C# WinForm 取消DataGridView的默认选中Cell 使其不反蓝
    我们 成就了每个我的世界
    [转]firefox与IE的事件处理
    C# WinForm CheckedListBox 使用的相关简单总结
    [书目20090216]高绩效人士的五项管理 李践作品
    [转]asp.net导出Excel/Csv格式数据最优方案(C#)
    [文摘20090201]男女朋友~~需记住亦舒的77句话
    WML 参考手册
    [引]ASP.NET 移动网页 与 如何:使用仿真程序和浏览器在部署移动 Web 应用程序之前对其进行测试
    [文摘20090203]3G知识入门讲座
  • 原文地址:https://www.cnblogs.com/ahl5esoft/p/12992059.html
Copyright © 2011-2022 走看看