Promise 借鉴了 Monad 的一些设计思想, 用 CPS 变换优化实现链式组合异步操作(解决异步的回调地狱)
async await 是类似于 Monad Comprehensions 的语法糖实现, 不过运行机制不一样, async await 依赖 javascript 运行时模型
原有的异步调用代码
asyncOp1((result1) => {
asyncOp2(result1, (result2) => {
asyncOp3(result2, (result3) => { /* ... */ });
});
});
Promise 优化之后的代码
asyncOp1()
.then((result1) => asyncOp2(result1))
.then((result2) => asyncOp3(result2))
.then((result3) => { /* ... */ });
使用 Monad Comprehensions 语法糖将中间变量从回调中取出来
result1 = await asyncOp1()
result2 = await asyncOp2(result1)
result3 = await asyncOp3(result2)
题外话, promise 不是标准的 monad, 但是有很多 monad 的特性, 比如统一的异常处理 (async await 之后就可以异步 try catch 了), 不然的话你想想每个回调都传一个 reject 参数来处理异常 (人肉 if err != nil 是吧)
所以说那些 go boy 用 if err != nil 来嘲笑 try catch 就挺逆天的, 学术界用几十年的时间来消除 errcode, 然后这玩意儿在 go boy 嘴里成最优雅的语言了😅
题外话 2, 其他语言的 Monad Comprehensions 语法糖举例
haskell
compute :: Maybe Int
compute = do
x <- Just 3
y <- Just 5
return (x + y) -- 结果为 Just 8
csharp
var query = from x in new[] { 1, 2 }
from y in new[] { 3, 4 }
select x + y; // 结果为 { 4, 5, 5, 6 }
fsharp
let asyncExample = async {
let! x = async { return 3 }
let! y = async { return 5 }
return x + y
}
scala
val compute: Option[Int] = for {
x <- Some(3)
y <- Some(5)
} yield x + y
swift
func compute() async -> Int {
let x = await fetchData()
let y = await processData(x)
return y
}
kotlin
suspend fun compute(): Int {
val x = async { fetchData() }
val y = async { processData(x.await()) }
return y.await()
}
java
没有😅
go
没有😅
rust
没有语法糖, 有个?(操作符)可以模拟类似功能(result option)
基本上后来兴起的工程向的语言都给异步相关的 api 实现了 monad comprehension, 一部分语言(fsharp kotlin 啥的)可以自定义语法
题外话 3
貌似一些 lisp people 极度反感 monad (和 go boy 殊途同归了属于是), clojure 社区里贴大字报明确反对标准库里添加 monad 实现, 吵了好几十条😅 |