您描述的问题有两个方面。
- 从外部看,您正在编写的程序应该整体上表现为异步的。
- 函数调用是否可能放弃控制在调用站点上应该不可见。
有两种方法可以实现此目的,但基本上可以归结为
- 具有多个线程(处于某种抽象级别)
- 在语言级别具有多种功能,所有这些功能都这样命名
foo(4, 7, bar, quux)
。
对于(1),我将分叉并运行多个进程,产生多个内核线程,以及将语言运行时级别线程调度到内核线程上的绿色线程实现。从问题的角度来看,它们是相同的。在这个世界上,从其线程的角度来看,没有函数会放弃或失去控制。在本身线程有时不具有控制,有时甚至没有运行,但你不放弃在这个世界上你自己的线程控制。符合此模型的系统可能具有或不具有产生新线程或加入现有线程的能力。适合该模型的系统可能具有复制 Unix之类的线程的能力,也可能没有能力fork
。
(2)很有趣。为了做到公正,我们需要谈论介绍和消除形式。
我将展示为什么await
不能以向后兼容的方式将隐式添加到Javascript之类的语言中。基本思想是,通过向用户公开承诺并在同步和异步上下文之间进行区分,Javascript泄漏了实现细节,从而阻止了统一处理同步和异步功能。还有一个事实,就是您不能await
在异步函数主体之外进行承诺。这些设计选择与“使调用者看不到异步性”不兼容。
您可以使用lambda引入同步函数,并通过函数调用消除它。
同步功能介绍:
((x) => {return x + x;})
同步功能消除:
f(4)
((x) => {return x + x;})(4)
您可以将其与异步函数的引入和消除进行对比。
异步功能介绍
(async (x) => {return x + x;})
消除异步函数(注意:仅在async
函数内部有效)
await (async (x) => {return x + x;})(4)
这里的根本问题是异步函数也是产生promise对象的同步函数。
这是在node.js repl中同步调用异步函数的示例。
> (async (x) => {return x + x;})(4)
Promise { 8 }
假设您可以使用一种语言,甚至是一种动态类型的语言,其中异步和同步函数调用之间的差异在调用站点上是不可见的,在定义站点上也可能不可见。
采用这样的语言并将其简化为Javascript是可能的,您只需要有效地使所有函数异步即可。