本文总计 1659 字,阅读大概需要 15 分钟。
疑问
为了解决丑陋的回调问题,基本上 Promise 已经成为标配。但是当 Promise 遇到 if-else 分支,需要走不同的链时如何处理?
举个栗子
if true
.then(do1())
.then(do2())
.then(do3())
.then(do4())
else
.then(do2())
.then(do3())
.then(do5())
这样的话用 Promise 怎么处理更优雅?
解答
当 Promise 遇到 if-else 分支总感觉不伦不类。因为 javascript 拥抱函数式编程的同时又不得不考虑到自己是命令式语言。
javascript 宣称自己可以面向对象、面向过程、面向函数。言外之意就是,厉害吧。而现实是,大部分 JSer 都在混合式编程,不伦不类,不三不四。
回到上面的问题:首先在写法上 .then(do1()).then(do2())
就是错误的,正确的写法应该是 .then(do1).then(do2)
——传入的是函数,而不是函数调用后的返回值。
另一方面,对比一下如下代码:
fn(true) { do1(); }
if(true) { do1(); }
除了 fn
和 if
两个单词之外,其它代码都相同。这两行代码如此的相似,但是却有着本质的区别。
有人可能会说这是废话,一个是函数,一个是 if
,当然不一样了啊。其实,这种区别只限于类 javascript 语言,在某些语言中,两者是没有任何区别的。
如果我们深入的研究这两者的区别,那就有意思了。。。
几年前我写过一篇博文,代码之谜(二)- 语句与表达式。对,他们的区别就在于,在 javascript 中, fn(true){do1();}
是表达式,而 if(true){do1();}
是语句。
表达式都有自己的值,而语句没有。因此我们可以写:
f = fn(true) { do1(); }
但是不能写:
f = if(true) { do1(); }
但是你千万不要把它看作是理所当然的。在某些语言,尤其是函数式编程语言中,if-else 也是表达式,就如同 m=1+2
,就如同 add(3,9)
。很多函数式编程语言的卖点就是只有表达式,没有语句。比如 Kotlin 中,你可以这么写:
val max = if (a > b) a else b
如果你在 javascript 中这么写的话,就报错了。
回到原始问题,如果我们不使用 if-else 语句,而是使用表达式,那么这个问题就迎刃而解了。我们可以定义 pIf
、 pElse
函数专门用于 promise。也可以定义一个对象,把 if
、 else
作为对象的属性(因为 javascript 的对象 key 都是字符串,因此可以使用 javascript 的关键字作为键名)。
如果我们使用 promise-conditional 库,写出来的代码是这样的:
doSomething()
.then(conditional()
.if(data => data.length > 10)
.then(doIfTrue)
.then(alsoDoIfTrue)
.elseIf(data => data.length > 5)
.then(doIfTrue)
.else()
.then(doIfFalse)
.end())
如果你不喜欢对象风格,喜欢函数风格的话,可以试试 p-if 库。
忘掉语句,拥抱表达式,享受函数式编程。