Generator的异步实现

整理了一下在学习和使用JS异步过程中的一些知识点。核心是在Generator实例的的回调中调度实例的下一步,同样的思想也能用于其它语言。比如Python中使用Generator实现了协程。C#早期有也这种TheadPool+Generator的异步实现。

什么是Generator

Generator 为生成器的意思,生成器函数的执行可以分段执行,函数在每一次调用后,函数只会执行到下一个位置再跳出函数。

Generator的简单使用

通过在申明方法的时候使用 * 和方法内使用yield 来表明自己的生成器函数身份。而调用生成器函数返回一个生成器。

function * buildGen(){
let r = yield 1;
console.log(`get: ${r}`);
return 2;
}
const gen = buildGen();
//gen 就是一个Generator,可以通过next 函数不断向下调用;
var next = gen.next();
console.log(next);
//{done:false,value:1}
next = gen.next("hello");
//get: hello
console.log(next);
//{done:true,value:2}

如上所示,调用buildGen后并没有立即执行,需要通过next函数才能工作,调用next()后返回一个显示当前完成状态和返回值的对象。

我们也可以通过向next 函数传递参数,完成向生成器传递信息。

Generator实现异步

通过Generator可以将JS的多重回调写法,变成同步写法。使异步使用更加易于理解。

function * buildGen(){
yield timeOutLogAsync(1000);
yield timeOutLogAsync(2000);
//yield readFileAsync('./test.txt');
}
function timeOutLogAsync(millseconds){
return (callback)=>{
setTimeout(callback,millseconds);
}
}
const gen = buildGen();
gen.next().value(()=>{
console.log(`first setTimeout is called`);
gen.next().value(()=>{
console.log(`sencond setTimeout is called`);
//gen.next();
});
});

以上代码还有一个问题,就是在需要手动执行next的来执行下一步,并且嵌套回调的结构也不利于阅读。不过可以通过引入自运行函数来解决这个问题。

function run(generator){
const gen = generator();
//内部实现了一个逆归调用
function next(err,value){
if(err){
throw err;
}
let handler = gen.next(value);
if(handler.done){
return;
}
handler.value(next);
}
next();
}
run(buildGen);

Thunk函数

在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。

//正常函数
fs.readFile(fileName,callback); const readFileThunk = function(fileName){
return (callback)=>{
fs.readFile(fileName,callback);
}
}
//thunk函数
readFileThunk(fileName)(callback);

thunkify 模块

thunkify模块提供了封装好的Thunk函数转换器。

npm install thunkify

使用如下

const thunkify = require("thunkify");
const fs = require("fs");
const readFileThunk = thunkify(fs.readFile);
readFileThunk('./test.txt')(function(err,value){
//...
});

源码见github

根据源码,thunkify 只能针对如fs.readFile(...args[],callback)这样将callback作为最后一个参数的标准函数,如果想要使用像setTimeout(callback,time)这样的函数,则需要我们做一个转换,如:

const executeTimeout = (time,callback) => setTimeout(callback,time);
const executeTimeoutThunk = thunkify(executeTimeout);
executeTimeoutThunk(1000)(function(){
console.log(new Date()) ;
});

co 模块

co模块是个流程管理模块。基于ES6的Generator和yield,能让我们用同步的形式编写异步代码。提供了上文中的run函数的作用。

npm install co


co(function*(){
var file1 = yield readFileThunk('./test.txt',{encoding:'utf8'});
console.log(file1);
var file2 = yield readFileThunk('./test2.txt',{encoding:'utf8'});
console.log(file2);
});

最新文章

  1. 乐校园单车项目第一天——购买Apple开发者账号、创建SVN
  2. SqlServer 分页查询
  3. Memcache服务器端参数说明
  4. Android--持久化技术
  5. DotNetBar v14.0.0.3 Fully Cracked
  6. jquery.pagination +JSON 动态无刷新分页
  7. 利用Hessian如何实现Webservice
  8. 剑指offer-第三章高质量的代码(输出该链表中倒数第K个节点)
  9. 如何:确定调用 ASP.NET 网页的方式
  10. FPGA那些事 --经典总结
  11. MySQL优化总结,百万级数据库优化方案
  12. php 常用的知识点归集(下)
  13. 单元测试(qunit)
  14. 开始翻译Disruptor
  15. BZOJ.2000.[HNOI2010]stone取石头游戏(博弈)
  16. Helm: Error: no available release name found
  17. 小甲鱼Python第十一讲课后习题
  18. eclipse在注释时候字体变成繁体字
  19. 浅拷贝与深拷贝的实现方式、区别;deepcopy如果你来设计,如何实现(一)
  20. arcgis license manager 10.2服务无法启动

热门文章

  1. Java线程池一:线程基础
  2. 题解 洛谷 P2612 【[ZJOI2012]波浪】DP+高精
  3. Clickhouse 入门
  4. js声明 对象,数组 的方法
  5. moviepy音视频开发:音频文件存取类AudioFileClip属性和方法介绍
  6. Python中迭代循环使用比较多的range函数的作用
  7. PyQt(Python+Qt)学习随笔:QListView的layoutMode属性和batchSize属性
  8. PyQt(Python+Qt)学习随笔:窗口的布局设置及访问
  9. ADF 第二篇:使用UI创建ADF
  10. Codeforces Edu Round 65 A-E