协程:意思是多个线程互相协作,完成异步应用。
{
function *asyncJob(){
//....dosomething
let f = yield readFile(fileA)
//...dosomething
}
}
上面的韩式asyncJob是一个协程,他的奥妙在于其中的yield命令。他表示执行到此处,执行权将交给其他携程
依旧是说,yield命令时异步两个阶段的分界线。
协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续向后执行。他的最大优点是代码的写法非常像同步
操作,如果去除yield命令,几乎一模一样。
5.2协程的Generator函数实现
Generator函数是协程再ES6中的实现,最大的特点就是可以交出函数的执行权。
整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方都用yield语句注明。
{
function* gen(x) {
let y = yield x + 2;
return y;
}
let g = gen(1);
g.next(); // {value:3,done:false}
g.next(); // {value:undefined,fone:true}
}
5.3Generator函数的数据交换和错误处理
Generator函数可以暂停执行和恢复执行,这是他能封装异步任务的根本原因。
除此之外还有他的内外的数据交换和错误处理机制。
next的返回值的value属性是Generator函数向外输出数据:next方法和可以接受参数,向Generator函数函数体内输入内容。
{
function* gen(x) {
let y = yield x + 2;
return y;
}
let g = gen(1);
g.next(); // {value:3,done:false}
g.next(2); // {value:2,fone:true}
}
Generator函数还可以部署错误处理代码,捕获函数体外抛出的错误。
{
function* gen(x){
try {
let y = yield x + 2;
} catch (e) {
console.log(e);
}
return y;
}
let g = gen(1);
g.next();
}
//这意味着代码内部外部的错误可以被函数体内的try...catch语句代码块捕获,这意味着出错的代码与处理错误的代码实现了时间和空间上的分离。
5.4 异步任务的封装
真实使用Generator函数执行一个异步任务
{
let fetch = require('node-fetch');
function* gen(){
let url = 'https:..api.github.com/users/github';
let result = yield fetch(url);
console.log(result.bio);
}
let g = gen();
let result = g.next();
result.value.then(function(data){
return data.json();
}).then(function(data){
g.next(data);
})
}
//上面的代码首先执行Generator函数获取遍历器对象,使用next方法(第二行)执行异步任务的第一阶段。由于Fetch模块返回的是一个Promise对象,因此要用then方法调用下一个next方法。
6.thunk函数
thunk函数是自动执行Generator函数的一种方法。
6.1thunk函数的含义
thunk函数属于传名调用,即参数进入函数体,再用到他的时候才求值。
{
function f(m){
return m + 2;
}
f(5);
//等同于
let thunk = function(){
return x + 5;
}
function f(thunk){
return thunk() + 2;
}
}
上述代码中,函数f的参数x+5被一个函数替换了。凡是用到原参数的地方,对Thunk函数求值即可。
这就是Thunk函数的定义,他是"传名调用"的一种实现策略。
7.JavaScript 语言的 Thunk 函数
在 JavaScript 语言中,Thunk 函数替换的是多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。
{
// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);
// Thunk版本的readFile(单参数版本)
var Thunk = function (fileName) {
return function (callback) {
return fs.readFile(fileName, callback);
};
};
var readFileThunk = Thunk(fileName);
readFileThunk(callback);
// Generator 函数自动执行,不过不适合异步操作。如果必须保证前一步执行完,才能执行后一步,则下面的自动执行就不可行。
function* gen() {
// ...
}
var g = gen();
var res = g.next();
while (!res.done) {
console.log(res.value);
res = g.next();
}
// Thunk 函数的自动流程管理
var fs = require('fs');
function run(generator) {
var it = generator(go);
function go(err, result) {
if (err) return it.throw(err);
it.next(result);
}
go();
}
run(function* (done) {
var firstFile;
try {
var dirFiles = yield fs.readdir('NoNoNoNo', done); // No such dir
firstFile = dirFiles[0];
} catch (err) {
firstFile = null;
}
console.log(firstFile);
});
}