当前位置: 首页 > 后端技术 > Node.js

thunk深入剖析

时间:2023-04-03 13:20:01 Node.js

一步步搭建thunkify本文思路:学习thunk相关知识,主要参考阮一峰的介绍一步步实现thunkify模块,并使用测试用例完善我们的代码以创建一个健壮的模块1.诞生背景Thunk函数的诞生源于编译器设计中的一个问题:求值策略,即函数的参数应该在什么时候求值。例如:varx=1;functionf(m){returnm*2;}f(x+5);其中要对表达式x+5进行求值,传值调用(callbyvalue)有两种方式,即在进入函数体之前,先计算出x+5的值,然后将这个值传给(6)进入函数f,如C语言。这种方式的优点是实现起来比较简单,但是可能会造成性能损失。按名称调用(callbyname),即直接将表达式(x+5)传入函数体,只在使用时才求值。2.Thunk函数的含义编译器的call-by-name实现往往是把参数放到一个临时函数中,然后把这个临时函数传递到函数体中。此临时函数称为Thunk函数。我们来看一个代码示例:functionf(m){returnm*2;}f(x+5);//等价于下面的代码varthunk=function(){returnx+5;};functionf(thunk){returnthunk()*2;}三、javascript中的Thunk函数我们都知道Javascript是按值调用的,那么js中的Thunk函数呢?在Javascript语言中,Thunk函数替代的不是表达式,而是多参数函数,将其替换为单参数版本,并且只接受一个回调函数作为参数。还是通过代码来理解,就是//普通版的readFile需要两个参数filename,callbackfs.readFile(fileName,callback);//readFilevar的thunk版本readFileThunk=thunkify(fs.readFile);readFileThunk(文件名)(回调);原文中的例子是柯里化的,预设参数fileName,直接调用fs.readFile就好了,现在我们来思考thunkify函数如何实现。从调用的形式来看,我们应该返回一个高阶函数,也就是返回一个函数a,而a的返回仍然是一个函数。varthunkify=function(fn){returnfunction(){returnfunction(){}}};结合上面的例子,因为是一个wrapperfunction,所以最后执行的是readFile,需要fileName,所以:varthunkify=function(fn){returnfunction(){varargs=Array.prototype.slice.call(arguments);返回函数(回调){args.push(回调);返回fn.apply(this,args);}}};完美,我们运行整个示例constfs=require('fs');varthunkify=function(fn){returnfunction(){varargs=Array.prototype.slice.call(arguments);返回函数(回调){args.push(回调);返回fn.apply(this,args);}}};varreadFileThunk=thunkify(fs.readFile);readFileThunk('test.txt','utf-8')((err,data)=>{console.log(data);});运行结果为4.创建一个thunkify模块要写一个健壮的thunkify功能,需要考虑各种情况,我们通过了tj写的thunkify模块的测试代码,来看看我们自己的thunkify还有哪些不足,逐步优化。1.保存上下文的问题functionload(fn){fn(null,this.name);}varuser={name:'tobi',load:thunkify(load)};user.load()((err,res)=>{console.log(res);});运行后res的结果是undefined,因为没有保存上下文,改进一下varthunkify=function(fn){returnfunction(){varargs=Array.prototype.slice.call(arguments);varctx=这个;返回函数(回调){args.push(回调);返回fn.apply(ctx,args);}}};2.捕获错误functionload(fn){thrownewError('boom');}load=thunkify(load);load()(err=>console.log(err.message));运行后发现没有捕获到错误,需要执行函数Performtry/catch,当出现错误时,传递错误信息。varthunkify=function(fn){returnfunction(){varargs=Array.prototype.slice.call(arguments);varctx=这个;返回函数(回调){args.push(回调);变量结果;//try/catch捕获信息,当错误发生时,传递给回调函数try{result=fn.apply(ctx,args);}catch(e){回调(e);}返回结果;}}};3.回调函数只能调用一次。函数加载(fn){fn(空,1);fn(null,2);fn(null,3);}load=thunkify(load);load()((err,ret)=>console.log(ret));运行的输出结果是123,但是我们期望结果只有1,那么我们需要判断回调是否执行过,让它只执行一次。varthunkify=function(fn){returnfunction(){varargs=Array.prototype.slice.call(arguments);varctx=这个;返回函数(回调){var已调用;//封装回调使得只能执行一次。args.push(function(){if(called)return;called=true;callback.apply(null,arguments);});变量结果;尝试{结果=fn.apply(ctx,args);}抓住(e){回调(e);}返回结果;}}};至此,我们已经通过了所有的测试,完成了一个健壮的thunkify模块。5.小结在学习一个概念或一个模块时,通过测试代码来加深对知识的理解和掌握。来源Thunk-阮一峰thunkify-tj