JS Thunk 函数的含义和用法实例总结
本文实例讲述了JSThunk函数的含义和用法。分享给大家供大家参考,具体如下:
前面我们已经学习过了Generator函数的优势和使用场景。
这篇文章我们继续学习阮老师的第二篇文章,Thunk函数的含义和用法
说实话,在这之前是没听过这个词的,但其实如果你对犀牛书里的不完全函数有认真看过的话
理解起来也不是很费劲。
首先什么是thunk函数?
很多场景下我们都会陷入一个问题,就是函数参数的求值时间。
是函数调用时即求值还是在函数内使用时才求值?
varx=1;
functionf(m){
returnm*2;
}
f(x+5)
//我们把在调用时就计算的方式称为传值调用,等同于:
f(6)
//我们把在函数内部使用时才求值的方式称为传名调用,等同于:
return(x+5)*2;
两种方式各有利弊,传值调用比较简单,但是如果计算后的结果没有在程序中使用的话,损失就有点大。
因此有很多场景都倾向于传名调用。
但是像C,java的编译方式都是固定的,如何基于现有基础改变程序的执行方式。
比较常见的是将想要传名调用的参数放到一个临时函数之中,把临时函数当做参数,只在使用的时候执行。
这个包装参数的临时函数就叫Thunk函数。我们试一下用Thunk函数改写一下上面的例子:
functionf(m){
returnm*2;
}
f(x+5);
//等同于
varthunk=function(){
returnx+5;
};
functionf(thunk){
returnthunk()*2;
}
其实这里我倒觉得可以翻翻犀牛书里的不完全函数,跟Thunk函数一个道理,
通过return一个function来实现传名调用。
老师也顺便介绍了用在生产环境的Thunkify模块
我们看一下源码,还是有一些好玩的地方的。
functionthunkify(fn){
//全局返回一个临时函数
returnfunction(){
varargs=newArray(arguments.length);
varctx=this;
for(vari=0;i
执行结果:
functionf(a,b,callback){
varsum=a+b;
callback(sum);
callback(sum);
}
varft=thunkify(f);
ft(1,2)(console.log);
//3
这个地方的理解,方法f在执行时的参数并不是1,2,console.log
console.log参数在thunkify内部被重新包装,成了:
function(){
if(called)return;//对callback重新包装,控制callback只执行一次
called=true;
console.log.apply(null,arguments);
}
了解了Thunk函数之后,我们要停下来想一想,还是那句话,它的出现要解决什么问题?
是不是一定要使用Thunk函数?Thunk用在什么场景下?
从前面的内容来看,其实并没有什么用,可能概念比较新颖,但是使用起来好像并没有太多提高。
但是没用的话我们也不会写这么一篇文章。
自从有了Generator函数,Thunk函数现在可以用于Generator函数的自动流程管理。
看一下例子:
varfs=require('fs');
varthunkify=require('thunkify');
varreadFile=thunkify(fs.readFile);
vargen=function*(){
varr1=yieldreadFile('/etc/fstab');
console.log(r1.toString());
varr2=yieldreadFile('/etc/shells');
console.log(r2.toString());
};
这个例子中,我们使用yield将执行权交给下一个协程,那么就需要有一种方法把执行权在交还给当前函数
这种方法就是Thunk函数,因为它可以重新包装回调函数,我们可以自己写包装函数,将执行权交还给Generator函数。
为了对比,我们先看一下如果手动执行上面的代码会是什么样的:
varg=gen();//开始执行协程
varr1=g.next();//读取第一个文件
r1.value(function(err,data){//读取完成执行回调
if(err)throwerr;
varr2=g.next(data);//读取第二个文件
r2.value(function(err,data){//读取完成执行回调
if(err)throwerr;
g.next(data);//结束协程
});
});
不难发现,上面的代码其实就是将同一个回调函数传入value属性(next执行返回value和done)
我在看的时候就在想,这个value是属性啊,为什么可以执行?还传递参数?
慢慢理一理:
value属性是yield的返回值,gen中的yield返回的是一个Thunk函数,不是固定值。
所以可以执行value,看前面例子里的这句:ft(1,2)(console.log);
value就等同于ft(1,2)的返回值
传递的function回调等同于(console.log);
这么是不是就理解了?
Thunk函数真正的威力,在于可以自动执行Generator函数。下面就是一个基于Thunk函数的Generator执行器:
functionrun(fn){
vargen=fn();//自动开始协程
//对next进行包装,形成Thunk函数,遍历调用
functionnext(err,data){
varresult=gen.next(data);
if(result.done)return;
result.value(next);
}
next();
/*参考bootstrap的写法改写一下
!functionnext(err,data){
varresult=gen.next(data);
if(result.done)return;
result.value(next);
}();
*/
}
run(gen);
上面的写法很简单吧,这么改写之后就不需要你手动的去控制执行next的时机了
只需要执行run函数就行。但是要保证每一个yield后面都是一个Thunk函数,否则的话就不能自动执行了。
这一章的学习总结就结束了,我们学会了如何使用Thunk函数实现自动执行,但Thunk函数并不是Generator函数自动执行的唯一方案。
因为自动执行的关键是,必须有一种机制,自动控制Generator函数的流程,接收和交还程序的执行权。回调函数可以做到这一点,Promise对象也可以做到这一点。
下一篇文章我们去看一下基于promise实现的自动执行器:co
原文:Thunk函数的含义和用法
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。