• 请不要在回答技术问题时复制粘贴 AI 生成的内容
kuoruan
V2EX  ›  程序员

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

  •  
  •   kuoruan · May 3, 2018 · 7895 views
    This topic created in 2959 days ago, the information mentioned may be changed or developed.

    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,但是代码的继续执行依赖于上一次请求的成功执行。 可苦于没有好的优化方案,只能写出这种垃圾代码了。

    Supplement 1  ·  May 3, 2018

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

    51 replies    2018-05-04 15:30:16 +08:00
    hkllzh
        1
    hkllzh  
       May 3, 2018
    Rx
    sunny352787
        2
    sunny352787  
       May 3, 2018   ❤️ 1
    最简单的,反过来判断不就行了
    kuoruan
        3
    kuoruan  
    OP
       May 3, 2018
    @sunny352787 #2 具体如何?
    hcymk2
        4
    hcymk2  
       May 3, 2018
    首先不再使用匿名函数。之后再找个对付回调地狱轮子
    Luckyray
        5
    Luckyray  
       May 3, 2018
    重构,封装成函数
    PressOne
        6
    PressOne  
       May 3, 2018 via Android
    有的时候多层嵌套少不了 ,不用故意减少。按实际的逻辑表现即可,你这个看起来也确实多了点
    kuoruan
        7
    kuoruan  
    OP
       May 3, 2018
    @Luckyray #5 封装成函数并没有事实上减少嵌套层级
    noli
        8
    noli  
       May 3, 2018 via iPhone
    你需要使用 c#或者像 c#那样提供 yield return 并且提供像 select 和 select many 把深层次数据提取代码 压扁的语法糖
    Phariel
        9
    Phariel  
       May 3, 2018
    Promise
    glacer
        10
    glacer  
       May 3, 2018
    Promise 或 async/await 了解一下
    widdy
        11
    widdy  
       May 3, 2018
    @kuoruan
    @sunny352787 意思是先判断 else 逻辑,把 else 提前 return;
    siteshen
        12
    siteshen  
       May 3, 2018   ❤️ 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  
       May 3, 2018
    @kuoruan
    回你 7l
    感觉你有点钻牛角尖了.重构和封装的确不会直接减少嵌套和层级,但是我们做的这些能直接提高代码可读性.
    而且你那个看起来好像可以用抛出异常的方式处理
    kuoruan
        14
    kuoruan  
    OP
       May 3, 2018
    @glacer #10 为了兼容性,用不了 ES6
    autoxbc
        15
    autoxbc  
       May 3, 2018
    Promise +1

    另外,(new XHR()).get...
    这种多余的括号看着真难受
    kuoruan
        16
    kuoruan  
    OP
       May 3, 2018
    @Immortal #13 这确实是个好方法
    AV1
        17
    AV1  
       May 3, 2018
    Promise,Babel 和 TypeScript 都支持 es3 target 的。

    如果不想用 ES6 语法,就用 async.js 。
    crab
        18
    crab  
       May 3, 2018
    if (x && x.status != 200)
    XXXXX
    RicardoScofileld
        19
    RicardoScofileld  
       May 3, 2018
    最简单的反向判断啊,if ...,你改成 if not ... 不就好咯嘛
    loveCoding
        20
    loveCoding  
       May 3, 2018
    反向判断+++1
    zn
        21
    zn  
       May 3, 2018 via iPhone   ❤️ 2
    短路疗法了解一下。



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

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

    其实你不知道你调用的代码库背后已经有了多少层的逻辑判断。
    evitceted
        24
    evitceted  
       May 3, 2018
    rx
    yongjing
        25
    yongjing  
       May 3, 2018
    callback hell
    sampeng
        26
    sampeng  
       May 3, 2018
    封装成函数+promise 可以解决问题。。但 promise 也可能陷入调用链太长的问题。总比这样强不是。

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

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

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

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

    kcptun.download().then(extract).then(move).catch(onError)
    f0rger
        49
    f0rger  
       May 4, 2018
    大概长这样,没调,可能有语法和写法错误:
    https://gist.github.com/luoweihua7/58b884ce7ecd618bb27fbe8fa9b4cf72
    zhuweiyou
        50
    zhuweiyou  
       May 4, 2018
    一个是用 async / await

    一个是先 return 错误的,就不会嵌套多层了
    bangbaoshi
        51
    bangbaoshi  
       May 4, 2018
    ·····
    if (cond1) {
    if (cond2) {
    if (cond3) {
    do something
    }
    }
    }
    ·····
    改成
    ·····
    if(!cond1) {
    return;
    }
    if(!cond2) {
    return;
    }
    if(!cond3) {
    return;
    }
    do something
    ·····
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1696 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 119ms · UTC 16:31 · PVG 00:31 · LAX 09:31 · JFK 12:31
    ♥ Do have faith in what you're doing.