任何一段程序的执行都有三种可能,成功、失败和超时。如果只处理了成功,这段程序只能称其为可用。如果处理了失败,那可称其为精良。如果处理了超时,可称无暇。
--- 某问,为何要写无瑕的程序?
--- 若问,JavaScript如何写出无暇的程序?
为何要写无瑕的程序。即为自己也为别人,主要还是为了自己。如同做一道饭菜。首先自己要觉得好吃,一是为填饱肚子,二是为了享受美食,对美食色香味的艺术追求。其次让别人也觉得好吃,别人也会欣赏你的美食,从而欣赏你。这样,人人能够看懂并欣赏你的代码。无瑕的代码自己写着舒服,糟糕的代码只会让你跳槽或者沉沦。
JavaScript 如何写出无暇的程序。遵从 JavaScript 三角形原则和六条军规,就可以做到战术上的无瑕。
为了提升语言的品质,JavaScript 社区近年出现的一些技术,甚至还引入了编译器。这些技术中,有些技术表达能力强,灵活。用它们足以写出无暇的程序。
其中有两项技术非常锋利,就是 Promise 和 Generator (Generator又被编译插件简化为async/await)。使用这两项锋利的技术,需要一些手法。如果用不好,会反被其伤。
当你写的代码中出现三角形的时候。要注意啦,你的代码开始恶化,开始变质。 出现三角形的原因多种多样,要想抹掉它,没有统一的解法。 此时,需要梳理你的逻辑,调整你的代码。使你的意图和你的代码的表现力到达平衡。 从而抹掉这个三角形。 下面五条军规,能帮助你抹掉大部分的三角形。
什么是三角形代码?例如
mysql.connect(ip, (e, r) => {
mysql.use(db, (e, r) => {
mysql.select(sql, (e, r) => {
mysql.insert(sql, (e, r) => {
ok
})
})
})
})
我经常见到下面的代码,它不敢直接使用 API 的 Promise
return new Promise((resolve, reject) => {
fetch().then(() => {
do_something
do_tomething
resolve()
})
})
正确的做法是
return fetch()
.then(do_something)
.then(do_tomething)
为什么不直接返回 API 的 Promise 呢?原因是他的思想还停留在教科书的例子上,或者停留在回调函数的年代中。 虽然用的是 Promise,但是还是在写回调函数。揉来揉去,弄巧成拙。
用这种错误方式写出的代码,会成为更加恐怖的回调地狱,违反三角形原则。
所以,这样做,是因为三角形原则。
if(success) {
do_something
} else {
do_tomething
}
不如写成
if(success) return do_something
do_tomething
在其他语言中,人们很自然会提前返回,这么做很自然。但是 JavaScript 中不是,人们可能写回调太多,总喜欢把逻辑包起来。提前返回能够抹掉三角形。
所以,这么做,还是因为三角形原则。
我常见到这样的代码,当异常时
return {};
。
他的想法是,成功的返回一定有值,那么失败的返回就是“空值”。
有这样想法的作者还处在很低的水平上。
如果程序异常,就应放心大胆的 throw。 如果你不知道怎么处理异常,就不要处理他,异常会自然的抛出去。
大胆的 throw 不会让你的程序出现福尔摩斯才能破解的难题。
throw 会让逻辑提前结束,相当于提前返回。
所以,这么做,还是因为三角形原则。
很多人会质疑为什么不用 Promise.all
。
如果你想并发的执行,那么直接执行Promise就好了。它本身就是异步的,加上await才会变成同步。
Promise.all
违反了三角形原则。
因为 Promise.all
有让代码恶化、类似食物变质一样的因子。
人们慢慢的会把 Promise 代码片段写在 Promise.all
的参数中,代码又变成三角形了。
这是第一个理由。
- 第二个理由,
Promise.all
不会真的变快,因为 JavaScript 是单核的。这个理由很弱,因为太底层了。前端程序员可能更在乎并发的 Ajax。 - 第三个理由,并发的 Ajax 确实会快。但是不是好的设计。任何程序都不应给它的宿主太大的压力,平滑的运算即有利宿主,也有利用户体验。
- 第四个理由,
Promise.all
的异常可能不是你想要的异常。因为它本身是个 Promise,只能被决定一次。 - 第五个理由,
Promise.all
没有超时,可能永远处于未决定状态。
主要理由是第一个理由。 所以,这么做,还是因为三角形原则。
很多人会激烈的反对这条规则。因为他担心实际做不到,所以反对。
我设定这条规则,肯定自己是严格遵守的,没有特别难受。
用 Promise 来写逻辑的话,没有哪种场景是非要用 try catch
不可的。
try catch
本身是一个小三角形,因此它违反了三角形原则,这是第一个理由。
- 第二个理由,
try catch
包不住异步逻辑中的异常,这会给人错觉。 - 第三个理由,事实证明,很多人懒得去找 bug,会粗暴的把大段代码用
try catch
包起来。 - 第四个理由,事实再次证明,很多人写
try catch
只是为了把错误秘起来,让代码继续。这种做法,被其他人效法,会像病毒一样蔓延。
Promise 的 catch 完全能胜任捕获异常的任务,并且,就应该用它来捕获异常。 为什么舍近求远呢?
Promise 的 catch 是链式的,而不像 try catch
是三角形的。
所以,这么做,还是因为三角形原则。
应该还有第六条军规吧,那就是你自己的发现喽!别忘了告诉我噢。