generator与async/await

之前就一直在困惑,await 后面包的那一大群代码到底是怎么个处理,闲下来了,这就学习一波

async和await是generator和Promise的语法糖,本质上是是将 Generator 函数和自动执行器,包装在一个函数里。

1
2
3
4
5
6
7
8
9
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}

spawn函数的实现,是Thunk函数的一种体现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function spawn(genF){
return new Promise(function(trdolve, reject){
const gen = genF();
function step(nextF){
let next;
try{
next = nextF();
}catch(e){
return reject(e);
};
if(next.done){
return resolve(next.value);
};
Promise.resolve(next.value).then(function(v){
step(function(){ return gen.next(v); });
},function(e){
step(function(){ return gen.throw(e); });
});
}
step(function(){ return gen.next(undefined); });
})
}

Thunk函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let x = 1;
function f(m){
return m*2;
}
f(x+5)

// 等同于
let thunk = function(){
return x+5;
}

function f(thunk){
return thunk() * 2;
}

编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数

Thunk函数的一大亮点就是其自动流程管理,也是Generator函数自动执行器的实现原理。

通过thunk函数,我们能够自动化交付控制权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function run(fn){
let gen = fn();
function next(err, data){
let result = gen.next(data);
if(result.done) return;
result.value(next);
}
next();
}

function* g(){
// ...
};

run(g);

我们都知道generator函数本身的执行需要通过next方法配合yield操作指针来放行任务,将控制权交接。但async和await并未出现相关的操作,只需要我们声明async函数和await的代码即可,函数会按顺序依次执行。

这本质上是通过thunk函数的自动执行的特点和Promise的状态实现的。
通过thunk函数,我们可以不用手动调用next,而是通过判断上一次next的执行结果是否结束(done是否为true)来继续执行或跳出

在Promise的Pending状态发生改变后,如果是 resolve,则通过Promise的then方法,生成一个新的Peomise对象,链式、递归的去执行下一个next方法,然后等待下一个Promise的状态改变,继续执行next,直到发现done为true为止

作者

徐云飞

发布于

2023-02-05

更新于

2023-02-05

许可协议

评论