让人头痛的Generator 函数的异步应用真的有用吗?( 四 )
上面代码中 , fs模块的readFile方法是一个多参数函数 , 两个参数分别为文件名和回调函数 。 经过转换器处理 , 它变成了一个单参数函数 , 只接受回调函数作为参数 。 这个单参数版本 , 就叫做 Thunk 函数 。
任何函数 , 只要参数有回调函数 , 就能写成 Thunk 函数的形式 。 下面是一个简单的 Thunk 函数转换器 。
// ES5版本var Thunk = function(fn){return function (){var args = Array.prototype.slice.call(arguments);return function (callback){args.push(callback);return fn.apply(this, args);}};};// ES6版本const Thunk = function(fn) {return function (...args) {return function (callback) {return fn.call(this, ...args, callback);}};};使用上面的转换器 , 生成fs.readFile的 Thunk 函数 。
var readFileThunk = Thunk(fs.readFile);readFileThunk(fileA)(callback);下面是另一个完整的例子 。
function f(a, cb) {cb(a);}const ft = Thunk(f);ft(1)(console.log) // 1Thunkify 模块生产环境的转换器 , 建议使用 Thunkify 模块 。
首先是安装 。
$ npm install thunkify使用方式如下 。
var thunkify = require('thunkify');var fs = require('fs');var read = thunkify(fs.readFile);read('package.json')(function(err, str){// ...});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];}return function (done) {var called;args.push(function () {if (called) return;called = true;done.apply(null, arguments);});try {fn.apply(ctx, args);} catch (err) {done(err);}}}};它的源码主要多了一个检查机制 , 变量called确保回调函数只运行一次 。 这样的设计与下文的 Generator 函数相关 。 请看下面的例子 。
function f(a, b, callback){var sum = a + b;callback(sum);callback(sum);}var ft = thunkify(f);var print = console.log.bind(console);ft(1, 2)(print);// 3上面代码中 , 由于thunkify只允许回调函数执行一次 , 所以只输出一行结果 。
Generator 函数的流程管理你可能会问 ,Thunk 函数有什么用?回答是以前确实没什么用 , 但是 ES6 有了 Generator 函数 , Thunk 函数现在可以用于 Generator 函数的自动流程管理 。
Generator 函数可以自动执行 。
function* gen() {// ...}var g = gen();var res = g.next();while(!res.done){console.log(res.value);res = g.next();}上面代码中 , Generator 函数gen会自动执行完所有步骤 。
但是 , 这不适合异步操作 。 如果必须保证前一步执行完 , 才能执行后一步 , 上面的自动执行就不可行 。 这时 , Thunk 函数就能派上用处 。 以读取文件为例 。 下面的 Generator 函数封装了两个异步操作 。
var fs = require('fs');var thunkify = require('thunkify');var readFileThunk = thunkify(fs.readFile);var gen = function* (){var r1 = yield readFileThunk('/etc/fstab');console.log(r1.toString());var r2 = yield readFileThunk('/etc/shells');console.log(r2.toString());};上面代码中 , yield命令用于将程序的执行权移出 Generator 函数 , 那么就需要一种方法 , 将执行权再交还给 Generator 函数 。
这种方法就是 Thunk 函数 , 因为它可以在回调函数里 , 将执行权交还给 Generator 函数 。 为了便于理解 , 我们先看如何手动执行上面这个 Generator 函数 。
推荐阅读
- 雷军发布会上的话!让人看到了雷军的“另一面”!网友:有良心
- 机器人送雪球!哈尔滨这场专业“打雪仗”,太让人羡慕了
- 2021年的中兴有多让人期待?倪飞在新年贺词中表了态
- XSX主机散热、噪声实机测试 出色表现让人满意
- 华为 WATCH FIT 体验:只有 34 克重,一款愿意让人戴着睡觉的智能手表
- 华为越南代言人走红,手机好不好用不清楚,让人挪不开眼睛
- 微信右上角+号隐藏5个黑科技功能,一键开启,让人相见恨晚
- 支付宝和微信支付,到底哪个使用人数更多呢?答案让人比较意外
- iPhone 11再度降价,直降700让人觉得有些太不保值
- iPhone12来了PVD工艺让人亮眼
