协程
多个线程互相协作,完成异步任务。yield命令表示执行到此处,执行权将交给其他协程,也就是说,yield命令是异步两个阶段的分界线。协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。
function *asyncJob() {
// ...其他代码
var f = yield readFile(fileA);
// ...其他代码
}
Generator 函数实现协程
function* gen(x) {
var y = yield x + 2;
return y;
}
var g = gen(1); //返回一个遍历器对象 不返回结果
//指向yield并执行,value是后面表达式的值。next方法的作用是分阶段执行Generator函数
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
Generator 函数的数据交换和错误处理
-
next接受参数进行数据交换
next返回值的value属性,是 Generator 函数向外输出数据;next方法还可以接受参数,向 Generator 函数体内输入数据。next接受的参数会被当做上一个yield的返回结果function* gen(x) { var y = yield x + 2; return y; } var g = gen(1); g.next() // { value: 3, done: false } g.next(2) // { value: undefined, done: true } //在这里传入的2会被当做是上一个yield的返回结果,即yield x+2等于2,这个时候y=2并返回
-
错误处理
Generator 函数体外,使用指针对象的
throw
方法抛出的错误,可以被函数体内的try...catch
代码块捕获function* gen(x){ try { var y = yield x + 2; } catch (e){ console.log(e); } return y; } var g = gen(1); g.next(); g.throw('出错了'); // 出错了
异步任务的封装
var fetch = require('node-fetch');
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url); //fetch读取接口,返回一个 Promise 对象
console.log(result.bio);
}
var g = gen();
var result = g.next(); //执行fetch(url),返回具有value和done属性的result对象
//在这里value相当于fetch返回的Promise对象,data就是接口返回的结果
result.value.then(function(data){
return data.json();
}).then(function(data){
g.next(data);
});
Thunk 函数
背景:函数参数的求值策略
var x = 1;
function f(m){
return m * 2;
}
f(x + 5)
//1. 传值调用
在进入函数体前就先计算x+5,成f(6),6再传进去执行,即6*2
//2. 传名调用
直接将表达式x+5传入,即(x+5)*2
Thunk 函数的含义
Thunk 函数是自动执行 Generator 函数的一种方法。编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数
function f(m) {
return m * 2;
}
f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};//相当于参数被替换成一个函数
function f(thunk) {
return thunk() * 2;
}
任何函数,只要参数有回调函数,就能写成 Thunk 函数的形式。下面是一个简单的 Thunk 函数转换器
function f(a, cb) {
cb(a);
}
let ft = Thunk(f); //转交给Thunk暂存
let log = console.log.bind(console); //会作为表达式放进去而不会立即执行
ft(1)(log) // 1
Thunkify 模块
生产环境的转换器,建议使用 Thunkify 模块。首先安装模块
$ npm install thunkify
使用
var thunkify = require('thunkify');
var fs = require('fs');
var read = thunkify(fs.readFile);
read('package.json')(function(err, str){
// ...
});
co模块
用于 Generator 函数的自动执行,
这个 Generator 函数用于依次读取两个文件
var gen = function* () {
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
co 模块可以让你不用编写 Generator 函数的执行器
var co = require('co');
co(gen);
co
函数返回一个Promise
对象,因此可以用then
方法添加回调函数
co(gen).then(function (){
console.log('Generator 函数执行完成');
});