如何才能减少代码的嵌套层级? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
kuoruan
V2EX    程序员

如何才能减少代码的嵌套层级?

  •  
  •   kuoruan 2018-05-03 14:51:44 +08:00 7441 次点击
    这是一个创建于 2719 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Linus Torvalds:

    If you need more than 3 levels of indentation, you ’ re screwed anyway, and should fix your program.

    如果你的代码里需要有超过三层的缩进,那么你已经搞砸了,应该修改你的代码。

    这里贴一段我写的代码

    function do_kcptun_update(btn) { btn.disabled = true; btn.value = '<%:Downloading...%>'; add_remove_page_notice(true); var kcptun_update_url = '<%=dsp.build_url("admin/services/kcptun/update/kcptun")%>'; (new XHR()).get(kcptun_update_url, { token: token_str, url: kcptun_info ? kcptun_info.url.download : '' }, function (x, json) { if (x && x.status == 200) { if (json.code == 0) { btn.value = '<%:Extracting...%>'; (new XHR()).get(kcptun_update_url, { token: token_str, task: "extract", file: json.file, subfix: kcptun_info ? kcptun_info.type : '' }, function (x, json) { if (x && x.status == 200) { if (json.code == 0) { btn.value = '<%:Moving...%>'; (new XHR()).get(kcptun_update_url, { token: token_str, task: "move", file: json.file }, function (x, json) { if (x && x.status == 200) { if (json.code == 0) { on_update_success(btn); } else { on_request_error(btn, json); } } else { on_request_error(btn); } }); } else { on_request_error(btn, json); } } else { on_request_error(btn); } }); } else { on_request_error(btn, json); } } else { on_request_error(btn); } }); } 

    这是一段用来更新程序的 Javascript,但是代码的继续执行依赖于上一次请求的成功执行。 可苦于没有好的优化方案,只能写出这种垃圾代码了。

    第 1 条附言    2018-05-03 16:24:30 +08:00

    稍微优化了一下,感觉能看一点了:GitHub

    51 条回复    2018-05-04 15:30:16 +08:00
    hkllzh
        1
    hkllzh  
       2018-05-03 14:54:14 +08:00
    Rx
    sunny352787
        2
    sunny352787  
       2018-05-03 14:55:27 +08:00   1 div class="sep5">
    最简单的,反过来判断不就行了
    kuoruan
        3
    kuoruan  
    OP
       2018-05-03 14:57:36 +08:00
    @sunny352787 #2 具体如何?
    hcymk2
        4
    hcymk2  
       2018-05-03 14:57:42 +08:00
    首先不再使用匿名函数。之后再找个对付回调地狱轮子
    Luckyray
        5
    Luckyray  
       2018-05-03 14:58:01 +08:00
    重构,封装成函数
    PressOne
        6
    PressOne  
       2018-05-03 14:59:32 +08:00 via Android
    有的时候多层嵌套少不了 ,不用故意减少。按实际的逻辑表现即可,你这个看起来也确实多了点
    kuoruan
        7
    kuoruan  
    OP
       2018-05-03 15:01:52 +08:00
    @Luckyray #5 封装成函数并没有事实上减少嵌套层级
    noli
        8
    noli  
       2018-05-03 15:05:03 +08:00 via iPhone
    你需要使用 c#或者像 c#那样提供 yield return 并且提供像 select 和 select many 把深层次数据提取代码 压扁的语法糖
    Phariel
        9
    Phariel  
       2018-05-03 15:05:32 +08:00
    Promise
    glacer
        10
    glacer  
       2018-05-03 15:07:42 +08:00
    Promise 或 async/await 了解一下
    widdy
        11
    widdy  
       2018-05-03 15:12:15 +08:00
    @kuoruan
    @sunny352787 意思是先判断 else 逻辑,把 else 提前 return;
    siteshen
        12
    siteshen  
       2018-05-03 15:14:35 +08:00   4
    楼上说的“最简单的,反过来判断不就行了”,其实说的是 `early return`。
    其他的回答 Promise async/await 等完全是另外一个话题。
    按照这个思路重构代码会清晰很多,刚刚写了个例子供参考。

    ```js
    // origin source code
    function doSomething() {
    if (cond1) {
    success1();
    if (cond2) {
    success2();
    if (cond3) {
    success3();
    } else {
    fail3();
    }
    } else {
    fail2();
    }
    } else {
    fail1();
    }
    }

    function betterDoSomething() {
    if (!cond1) {
    fail1();
    return;
    }
    success1();

    if (!cond2) {
    fail2();
    return;
    }
    success2();

    if (!cond3) {
    fail3();
    return;
    }
    success3();
    }
    ```
    Immortal
        13
    Immortal  
       2018-05-03 15:16:05 +08:00
    @kuoruan
    回你 7l
    感觉你有点钻牛角尖了.重构和封装的确不会直接减少嵌套和层级,但是我们做的这些能直接提高代码可读性.
    而且你那个看起来好像可以用抛出异常的方式处理
    kuoruan
        14
    kuoruan  
    OP
       2018-05-03 15:17:04 +08:00
    @glacer #10 为了兼容性,用不了 ES6
    autoxbc
        15
    autoxbc  
       2018-05-03 15:18:30 +08:00
    Promise +1

    另外,(new XHR()).get...
    这种多余的括号看着真难受
    kuoruan
        16
    kuoruan  
    OP
       2018-05-03 15:19:19 +08:00
    @Immortal #13 这确实是个好方法
    AV1
        17
    AV1  
       2018-05-03 15:23:21 +08:00
    Promise,Babel 和 TypeScript 都支持 es3 target 的。

    如果不想用 ES6 语法,就用 async.js 。
    crab
        18
    crab  
       2018-05-03 15:27:38 +08:00
    if (x && x.status != 200)
    XXXXX
    RicardoScofileld
        19
    RicardoScofileld  
       2018-05-03 15:28:29 +08:00
    最简单的反向判断啊,if ...,你改成 if not ... 不就好咯嘛
    loveCoding
        20
    loveCoding  
       2018-05-03 16:03:19 +08:00
    反向判断+++1
    zn
        21
    zn  
       2018-05-03 16:07:08 +08:00 via iPhone   2
    短路疗法了解一下。



    其实就是楼上说的反向判断啦,先判断不符合条件的,尽快返回,后面剩下合法条件再做处理。
    GoLand
        22
    GoLand  
       2018-05-03 16:07:56 +08:00   1
    early return.
    qiumaoyuan
        23
    qiumaoyuan  
       2018-05-03 16:16:07 +08:00
    @kuoruan 封装是唯一正确的办法。不仅仅是封装成方法,还可以封装成类。

    要嵌套多少层是业务逻辑决定的,而不是代码编写方式决定的。

    其实你不知道你调用的代码库背后已经有了多少层的逻辑判断。
    evitceted
        24
    evitceted  
       span class="ago" title="2018-05-03 16:22:48 +08:00">2018-05-03 16:22:48 +08:00
    rx
    yongjing
        25
    yongjing  
       2018-05-03 16:25:52 +08:00
    callback hell
    sampeng
        26
    sampeng  
       2018-05-03 16:48:45 +08:00
    封装成函数+promise 可以解决问题。。但 promise 也可能陷入调用链太长的问题。总比这样强不是。

    楼上有说封装成函数没减少层级。有些逻辑是没办法减少的。但封装函数最少一眼能看明白逻辑,让代码清晰可见的最终目的不就是可读性尚可。

    early return 也是一个很好的手段。稍微减少一些不必要判断层级。

    唔。。少用匿名函数。。简直是噩梦
    pluschen
        27
    pluschen  
       2018-05-03 16:56:22 +08:00
    啥?封装成函数没减少层数?
    别逗,哪有什么层数?都是 JMP。
    sampeng
        28
    sampeng  
       2018-05-03 16:58:58 +08:00
    仔细看了代码。。只是各种 else 处理错误而已。。。这个。。很难吗?
    xiaojunjor
        29
    xiaojunjor  
       2018-05-03 17:02:32 +08:00
    kuoruan
        30
    kuoruan  
    OP
       2018-05-03 17:12:59 +08:00
    @sampeng #28 不好意思,我能力不行,让您见笑了
    zhlssg
        31
    zhlssg  
       2018-05-03 17:16:50 +08:00
    async await ,提早 return
    saulshao
        32
    saulshao  
       2018-05-03 17:19:49 +08:00
    If you need more than 3 levels of indentation, you ’ re screwed anyway, and should fix your program.
    这句话本来就是说的是一个函数里面的事情。
    cs923
        33
    cs923  
       2018-05-03 17:31:50 +08:00
    RxJs(没用过 应该能解决)
    Justin13
        34
    Justin13  
       2018-05-03 18:21:09 +08:00 via Android
    就嵌套而言,可以考虑 early return,抽函数,就业务而言,可以考虑使用更优雅的库,promise,async
    we2ex
        35
    we2ex  
       2018-05-03 18:23:59 +08:00 via Android
    @xiaojunjor #29 正解,第一时间想到的也是这篇文章
    banricho
        36
    banricho  
       2018-05-03 18:25:14 +08:00
    这个代码的根本原因在于直接原生 XHR …

    不用任何库的情况下,封装一个 fetch
    剩下是处理异步的问题,楼上都说了
    最后才是 if 的问题
    wlwood
        37
    wlwood  
       2018-05-03 18:42:50 +08:00
    其实,感觉,用 react.js, vue 这些框框,这种地狱应该挺好解决啊
    noNOno
        38
    noNOno  
       2018-05-03 20:08:10 +08:00
    歪个楼,动感光波?
    Exia
        39
    Exia  
       2018-05-03 20:14:50 +08:00
    楼主的地狱回调,能自己看懂就还不错了,应该用 promise 吧,我也要向这个方面提升一下。
    Enivel
        40
    Enivel  
       2018-05-03 20:22:12 +08:00 via iPhone
    多个 return 也不好 可以用 do while ( false ) break 实现类似功能
    xpol
        41
    xpol  
       2018-05-03 22:19:51 +08:00
    GraphQL 了解一下。
    xpol
        42
    xpol  
       2018-05-03 22:23:05 +08:00
    我以为都是从服务器请求数据:),请忽略我上一条回复。
    mingyun
        43
    mingyun  
       2018-05-03 23:47:44 +08:00
    提前 return
    pepesii
        44
    pepesii  
       2018-05-04 08:41:10 +08:00 via iPhone
    我记得上初中的时候,数学老师经常说的一句话是,“正难则反”,于是我在碰到真值条件复杂的时候,就找假条件先处理,提前返回。
    crayygy
        45
    crayygy  
       2018-05-04 09:51:41 +08:00
    打开帖子以前看到标题的第一反应就是 JS 的回调地狱...
    th00000
        46
    th00000  
       2018-05-04 09:59:27 +08:00
    快速失败原则
    ikaros
        47
    ikaros  
       2018-05-04 10:03:53 +08:00
    你这样写以后再来看不觉得麻烦吗?
    SakuraKuma
        48
    SakuraKuma  
       2018-05-04 10:19:07 +08:00
    Promise 啊。

    kcptun.download().then(extract).then(move).catch(onError)
    f0rger
        49
    f0rger  
       2018-05-04 11:33:10 +08:00
    大概长这样,没调,可能有语法和写法错误:
    https://gist.github.com/luoweihua7/58b884ce7ecd618bb27fbe8fa9b4cf72
    zhuweiyou
        50
    zhuweiyou  
       2018-05-04 12:10:19 +08:00
    一个是用 async / await

    一个是先 return 错误的,就不会嵌套多层了
    bangbaoshi
        51
    bangbaoshi  
       2018-05-04 15:30:16 +08:00

    if (cond1) {
    if (cond2) {
    if (cond3) {
    do something
    }
    }
    }

    改成

    if(!cond1) {
    return;
    }
    if(!cond2) {
    return;
    }
    if(!cond3) {
    return;
    }
    do something
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2767 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 12:32 PVG 20:32 LAX 05:32 JFK 08:32
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86