本文实例讲述了JS Thunk 函数的含义和用法。分享给大家供大家参考,具体如下:
前面我们已经学习过了Generator 函数的优势和使用场景。
这篇文章我们继续学习阮老师的第二篇文章,Thunk 函数的含义和用法
说实话,在这之前是没听过这个词的,但其实如果你对犀牛书里的不完全函数有认真看过的话
理解起来也不是很费劲。
首先什么是 thunk 函数?
很多场景下我们都会陷入一个问题,就是函数参数的求值时间。
是函数调用时即求值还是在函数内使用时才求值?
var x = 1; function f(m){ return m * 2; } f(x + 5) //我们把在调用时就计算的方式称为传值调用,等同于: f(6) //我们把在函数内部使用时才求值的方式称为传名调用,等同于: return (x + 5) * 2;
两种方式各有利弊,传值调用比较简单,但是如果计算后的结果没有在程序中使用的话,损失就有点大。
因此有很多场景都倾向于传名调用。
但是像 C,java 的编译方式都是固定的,如何基于现有基础改变程序的执行方式。
比较常见的是将想要传名调用的参数放到一个临时函数之中,把临时函数当做参数,只在使用的时候执行。
这个包装参数的临时函数就叫 Thunk 函数。我们试一下用 Thunk 函数改写一下上面的例子:
function f(m){ return m * 2; } f(x + 5); // 等同于 var thunk = function () { return x + 5; }; function f(thunk){ return thunk() * 2; }
其实这里我倒觉得可以翻翻犀牛书里的不完全函数,跟 Thunk 函数一个道理,
通过 return 一个 function 来实现传名调用。
老师也顺便介绍了用在生产环境的 Thunkify 模块
我们看一下源码,还是有一些好玩的地方的。
function thunkify(fn){ //全局返回一个临时函数 return function(){ var args = new Array(arguments.length); var ctx = this; for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } //上面一段将参数copy到args return function(done){ var called; args.push(function(){ if (called) return; //对callback重新包装,控制callback只执行一次 called = true; done.apply(null, arguments); }); try { fn.apply(ctx, args); } catch (err) { done(err); } } } };
执行结果:
function f(a, b, callback){ var sum = a + b; callback(sum); callback(sum); } var ft = 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 函数的自动流程管理。
看一下例子:
var fs = require('fs'); var thunkify = require('thunkify'); var readFile = thunkify(fs.readFile); var gen = function* (){ var r1 = yield readFile('/etc/fstab'); console.log(r1.toString()); var r2 = yield readFile('/etc/shells'); console.log(r2.toString()); };
这个例子中,我们使用 yield 将执行权交给下一个协程,那么就需要有一种方法把执行权在交还给当前函数
这种方法就是 Thunk 函数,因为它可以重新包装回调函数,我们可以自己写包装函数,将执行权交还给 Generator 函数。
为了对比,我们先看一下如果手动执行上面的代码会是什么样的:
var g = gen(); //开始执行协程 var r1 = g.next(); //读取第一个文件 r1.value(function(err, data){ //读取完成执行回调 if (err) throw err; var r2 = g.next(data); //读取第二个文件 r2.value(function(err, data){ //读取完成执行回调 if (err) throw err; 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 执行器:
function run(fn) { var gen = fn(); //自动开始协程 //对next进行包装,形成 Thunk 函数,遍历调用 function next(err, data) { var result = gen.next(data); if (result.done) return; result.value(next); } next(); /* 参考bootstrap的写法改写一下 !function next(err, data) { var result = 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程序设计有所帮助。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]