thunkthunk形式上把函数的执行部分和回调部分分开了,这样我们就可以在一个地方执行执行函数,在另一个地方执行回调函数。这样做的价值在于,在做异步操作时,我们只需要知道回调函数的执行顺序和嵌套关系,就可以按顺序获取执行函数的结果。下面是一个简单的thunk实现:functionthunkify(fn){returnfunction(){varargs=Array.prototype.slice(arguments);varctx=这个;返回函数(完成){varcalled=false;args.push(function(){if(called)return;called=true;done.apply(null,arguments);});尝试{fn。应用(ctx,参数);}赶上(犯错){完成(犯错);}}上面的实现把函数原来的执行变成了分别执行“执行部分”和“回调部分”的方法:fn(a,callback)=>thunkify(fn)(a)(callback)例如:varfs=require('fs');varreadFile=thunkify(fs.readFile);//把readFile函数包装成thunkify,变成thunkify函数//**这是一个执行函数的集合**//varf1=readFile('./a.js');varf2=readFile('./b.js');varf3=readFile('./c.js');//**这是回调函数的集合**////使用嵌套来控制f1和f2的执行顺序f1(function(err,data1){//doSomethingf2(function(err,data2){//doSomethingf3(function(err,data3){//doSomething})})})而传统的写法是://传统的写法fs.readFile('./a.js',function(err,data1){//doSomethingfs.readFile('./b.js',function(err,data2){//doSomethingfs.readFile('./c.js',function(err,data3){//doSomething})})})执行部分和回调之后部分分离,可以使用generator等异步控制技术,方便流程控制,避免回调黑洞上面的文件读取过程可以用generator进行改造:varfs=require('fs');varreadFile=thunkify(fs.readFile);//**函数的'执行部分'一起执行**//vargen=function*(){vardata1=yieldreadFile('./a.js');//用户获取到数据后,写入这里console.log(data1.toString());vardata2=yieldreadFile('./b.js');//用户获取到数据后,写入这里console.log(data2);}//函数的‘回调部分’在另一个地方执行,和调用的形式一样varg=gen();vard1=g.next();//返回结果为{value:func,done:boolean}//执行value,实际执行`d1.value(callback)`//还有`thunkify(fs.readFile)('./a.js')(callback)`d1.value(function(err,data){if(err)throwerr;//g.next(data)可以将参数data传回生成器函数体,作为异步任务中的执行结果previousstage//在例子中,data作为data1的值传回gen函数体vard2=g.next(data);d2.value(function(err,data2){if(err)throwerr;g.next(data2);});});co在上面的修改中,发现执行回调部分时,仍然存在回调嵌套:d2.value在d1.value的回调中执行。经过观察,发现在callback执行的时候,也就是g执行next()的时候,执行形式基本一样,都是:d.value(function(err,data){if(err)throw错误;g.next(数据);});这种形式,因此您可以通过编写递归函数来组织流程。函数运行(fn){varg=fn();//下一步控制函数,实际上是d.value的回调函数functionnext(err,data){//将之前的数据传递给gen()函数varresult=g.next(data);//判断是否结束if(result.done)return;//下一句执行回调next时,递归result.value(next);}//执行第一步next();}//使用run(gen);上面代码中的过程很容易理解,就是把gen放在一个递归中执行,而这个递归中有一个核心函数next,就是一个递归函数。当函数中g.next(data)返回的done属性值为true时,表示当前generator函数中的yield已经执行完毕,可以退出了。当不为真时,说明当前generator函数还有未执行的yield,继续调用下一个函数继续同样的过程。以上流程是异步流控库co的一个简单实现。
