本文写作缘由

正文
at first, there was just normal code.
however, actual code need to interactive with outside. Like web requests, file IO.
so code becomes
when execute the
a_function_wait_for_respond
, the whole function stopped, waiting for respond.But we don’t want this, so let’s put them in background, and skip the waiting.
now it runs properly. But if
a_function_wait_for_respond
has some return value, how to get it?put it into callback with cause callback hell, so we introduce a new api called promise
but it’s not nature, we introduce
async
, await
but await will change the execute behavior, so we mark the function async.
Now it’s time to support this syntax.
From the upper analyse, we know async / await is just syntax sugar to the Promise.
so when execute async function, first scan the function, if there is no await, then it’s just normal function, execute, then quit.
if has await stmt. do some transform.
translate into
now the f becomes normal func.
so we split the function into two parts.
sync code (before promise)
continues (in promise)
continue doing it until the function finishes.
this trans called cps.
draft
考虑以下代码
首先打印 0
然后进入 g(1);
遇到 await f(x);
退出调用栈
打印 2;
恢复调用栈
开始执行 f(x)
打印 x
退出 f(x)
调用栈也空了,全部结束。
所以实现的重点是怎么“恢复现场”
如果直接尝试阅读 js spec, 大概率劝退。因为上面只描述了行为,没有怎么实现。
这里我们实现无栈协程
答案:cps
但是如果直接去阅读那本 compiling with continuation ,大概率也放弃。因为里面主要在讲怎么用 cps 表达语言里的各种 stmt,还有编译优化。
ok, 但是的确是用 cps, 那怎么操作呢?
仅考虑如下例子
把它解糖/ast 转换/ir 转换,you name it,变成
注意这里 f 就变成 normal func 了。
那 promise 又是啥?
是 JS 里的 Promise, C#,Python 里的 Task, Rust 里的 Future
但是严格来说也不是,因为这个 promise 只是做一个把任务压进 task queue 的动作。
好烦啊,还得介绍 promise, task queue
所以上面的步骤是
- f(x) 开始执行 f
- 执行 promise, 把 cont 续延包成一个新 func + env 复制一份压入 task queue,这份 Job 就记作 p, 同时原函数结束。
- 然后等别的 sync 的代码跑完了,task queue 开始收拾现场,弹出 p, 执行 p 里的 func
- 然后遇到 await 就递归把 await 后面的全包成 promise 压入队列。重复上面操作。
小彩蛋

关于怎么用 java 在 lox 里实现 async, 等后面有机会把代码附上好了。
题外话,那为啥 await 必须在 async 里呢
因为一般的函数都是同步模型,一行行执行下去,但是 await 需要解糖,在 ast / ir 做转换。也就是不能同步。后面的代码执行的时序是不一样的。
但是只在单个 async 内可以看成一样的,因为环境保留下来了。