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

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

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

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

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



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

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

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

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

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

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

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

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

    一个是先 return 错误的,就不会嵌套多层了
    bangbaoshi
    bangbaoshi
    May 4, 2018
    51
    ·····
    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   ·   Privacy   ·   Solana   ·   1940 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 142ms · UTC 08:10 · PVG 16:10 · LAX 00:10 · JFK 03:10
    ♥ Do have faith in what you're doing.