Koa 中异步方法的使用

使用 Generator 简化异步回调

1
2
3
4
5
6
7
8
9
10
11
12
13
var gen;
function async() {
setTimeout(function () {
gen.next(100);
});
}
function * genFn() {
var res = yield async();
console.log('res is: ' + res);
}

gen = genFn();
gen.next();

输出的结果是

1
res is: 100

如果将后面的 .next() 封装起来,就变成这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 当前的 Generator
var activeGenerator;

// 处理 g.next() 功能
function gNext() {
return function (err, data) {
if (err) {
throw err;
}
// 前文中的 g.next(),并把回调函数的结果作为参数传递给 yield
activeGenerator.next(data)
}
}

// 控制工具
function gQueue(generatorFunc) {
activeGenerator = generatorFunc(gNext());
activeGenerator.next();
}

function asyncFunc(cb) {
// 这个函数模拟一个异步操作,将在 1 秒后触发回调函数
setTimeout(function() {
cb(null, 100);
}, 1000)
}

// 声明一个 Generator 并传给 gQueue
gQueue(function * flow(next) {
console.log('start');

// 执行异步函数 asyncFunc,并把 next 注册在其回调函数里
var y = yield asyncFunc(next);

// 回调执行完成后,会触发 g.next(),此时 y 的值为 asyncFunc 回调里的 100
console.log('y is', y);

// 同上
var z = yield asyncFunc(next);
console.log('z is ', z);

console.log('end')
});

// console log
// start
// y is 100
// z is 100
// end

参考文章: 使用 (Generator) 生成器解决 JavaScript 回调嵌套问题

Koa 中的应用

koa 使用的就是上面的原理,按照 co 的用法,就可以这样

main.js

1
2
3
4
5
6
7
8
9
10
11
// ...

var template = require('./template');
// 区域根目录
app.use(function * (next) {
yield next;
// 渲染模板
this.body = yield template.tpl('tplName');
});

// ...

template.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
exports.tpl = function (path) {
return function (cb) {
var tmpl = new Template(path); // 某种模板渲染器的构造函数
// 成功异步回调
tmpl.on('success', function (res) {
cb(null, res);
});
// 失败异步回调
tmpl.on('error', function (err) {
cb(err, null);
});
};
};
// ...

注意到 gNext 输出的是如下形式的 function

1
2
3
function (err, data) {
// 处理 err 和 data
}

因此 template.js 中的 cb 便是上述形式。

参考文章: 探索Javascript异步编程