论文连接javascript
协程(coroutine)这一律念最先在1963年由Convay提出,虽然在上世纪80年代受到冷落,但在此以后,协程在Lua、Python、Ruby、Kotlin等诸多主流语言中都发挥了重要的做用。然而,包括Java、Swift等在内的不少语言并不能原生支持协程。本文做者提出了一种利用高阶函数来实现协程的方法,这能够应用于几乎全部编程语言。java
协程和函数很是相像,一般来讲,两者都接受若干参数,而后产生必定的结果。两者的区别在于,协程能够暂时挂起(一般会使用一个yield
语句),保留其运行环境,并将控制权转交给另外一个协程。在此以后,这一协程会从新得到控制权,继续运行直到再次挂起或终止。因为协程运行在同一个线程内,不存在资源共享的问题,无须使用锁,于是就避免了死锁的发生。编程
协程有不少不一样的实现方法。这些方法间一个重要的分野就在于协程的实现是(全)对称仍是半对称。所谓“(全)对称”,是指协程的切换能够发生在任意两个协程之间,而“半对称”则表示协程的切换只能在调用栈中直接的父/子(parent/child)之间发生。对称实现的协程有利于对整个流程进行更加全面的把控,而半对称的协程实现一般更容易理解、使用和调试。闭包
这两种实现方式在最终的效果上并无差别(参见de Moura & Ierusalimschy, 2009)app
另外一个重要的区别在于协程是不是“栈满”(stackfull)的。栈满的协程能够从调用栈的更深层被挂起,而非栈满的则不行。若是要在一个非栈满的协程实现环境下,编写一个异步非阻塞的程序,就要求全部函数都能做为生成器,而且全部的函数调用都要显式地调用内层嵌套函数的结果。异步
一样的,这两种实现方式在最终的效果上也没有差别。编程语言
本文方法是一个半对称非栈满的协程实现。函数
上图给出了一个在JavaScript中利用生成器(generator)来实现协程的例子。函数内部的while(true)
并不会一直运行,而是每次在yield
处暂时挂起,直到下一次的.next()
被调用时才继续进行。ui
在这一部分做者给出了一个更加简单的例子:利用协程实现两个元素的加法。spa
首先是生成器的实现:
function * f(n) {
const x = yield n
yield n + x
}
const add2 = f(2)
add2.next() //=>2
add2.next(3) //=>5
add2.next(5) //=>undefined (由于一共只有两次yield)
复制代码
接下来是高阶函数版本的实现:
function f(n) {
let inst = 1
return function(x) {
switch (inst) {
case 1 : inst += 1; return n
case 2 : inst += 1; return n + x
default: break
}
}
}
const add2 = f(2)
add2() //=>2
add2(3) //=>5
add2(5) //=>undefined
复制代码
利用高阶函数来实现协程的出发点很简单:大部分支持高阶函数的编程语言都实现了闭包(closure),而实现协程的关键就在于保存环境(现场),那么就能够借助于高阶函数的函数闭包,来保存协程挂起时的所在环境。与生成器版本相比,利用高阶函数实现的协程不须要使用额外的语法(function*
/yield
/next
)。
一样的,能够用高阶函数来实现上一节中Fibonacci序列的例子。
function fib() {
let inst = 1; let a = null; let b = null
return function() {
while (true) {
switch (inst) {
case 1:
inst = 2; a = 1; b = 2
case 2:
inst = 3; return a
case 3:
inst = 2; const c = a; a = b; b = c + a
}
}
}
}
const f = fib()
f() //=>1
f() //=>2
f() //=>3
f() //=>5
f() //=>8
复制代码
那么若是编程语言不支持闭包怎么办呢?那就须要自行实现相似闭包的保存环境机制,做者在附录中给出了一个C语言的例子,能够参考一下。
这篇文章的内容到这里就介绍完了。用高阶函数的方式实现协程是否是很清晰明了呢?感兴趣的话,就亲自尝试一下吧!
// Emulation of first order functions.
typedef struct {
void* env;
void* (*fn)(void*);
} function_t;
void* apply(function_t closure) {
return closure.fn(closure.env);
}
// Rewriting of the fibonacci sequence coroutine.
typedef struct {
int inst;
int a;
int b;
} fib_env;
void* fib_fo(void* e) {
fib_env* fe = (fib_env*)(e);
while (1) {
switch (fe->inst) {
case 1:
fe->inst = 2; fe->a = 1; fe->b = 2;
case 2:
fe->inst = 3; return &(fe->a);
case 3:
fe->inst = 2; int c = fe->a; fe->a = fe->b; fe->b = c + fe->a;
}
}
}
function_t fib() {
fib_env* env = (fib_env*)(malloc(sizeof(fib_env)));
env->inst = 1;
function_t closure = { env, &fib_fo };
return closure;
}
// Example of invocation.
int main() {
function_t g = fib();
for (int i = 0; i < 10; ++i) {
printf("%i\n", *(int*)(apply(g)));
}
free(g.env);
return 0;
}
复制代码