当前位置: 代码迷 >> 综合 >> 从 generator 函数 到 redux -saga (二)
  详细解决方案

从 generator 函数 到 redux -saga (二)

热度:55   发布时间:2023-11-21 05:20:40.0

Thunk 函数的自动流程管理

 

JavaScript 语言的 Thunk 函数  

JavaScript 语言是传值调用,它的 Thunk 函数含义有所不同。在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。

// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);// Thunk版本的readFile(单参数版本)
var Thunk = function (fileName) {return function (callback) {return fs.readFile(fileName, callback);};
};var readFileThunk = Thunk(fileName);
readFileThunk(callback);

上面代码中,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);

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 函数。

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);});
});

上面代码中,变量g是 Generator 函数的内部指针,表示目前执行到哪一步。next方法负责将指针移动到下一步,并返回该步的信息(value属性和done属性)。

仔细查看上面的代码,可以发现 Generator 函数的执行过程,其实是将同一个回调函数,反复传入next方法的value属性。这使得我们可以用递归来自动完成这个过程

Thunk 函数的自动流程管理

Thunk 函数真正的威力,在于可以自动执行 Generator 函数。下面就是一个基于 Thunk 函数的 Generator 执行器。

function run(fn) {var gen = fn();function next(err, data) {var result = gen.next(data);if (result.done) return;result.value(next);}next();
}function* g() {// ...
}run(g);

上面代码的run函数,就是一个 Generator 函数的自动执行器。内部的next函数就是 Thunk 的回调函数。next函数先将指针移到 Generator 函数的下一步(gen.next方法),然后判断 Generator 函数是否结束(result.done属性),如果没结束,就将next函数再传入 Thunk 函数(result.value属性),否则就直接退出。

有了这个执行器,执行 Generator 函数方便多了。不管内部有多少个异步操作,直接把 Generator 函数传入run函数即可。当然,前提是每一个异步操作,都要是 Thunk 函数,也就是说,跟在yield命令后面的必须是 Thunk 函数

var g = function* (){var f1 = yield readFileThunk('fileA');var f2 = yield readFileThunk('fileB');// ...var fn = yield readFileThunk('fileN');
};run(g);

上面代码中,函数g封装了n个异步的读取文件操作,只要执行run函数,这些操作就会自动完成。这样一来,异步操作不仅可以写得像同步操作,而且一行代码就可以执行。

Thunk 函数并不是 Generator 函数自动执行的唯一方案。因为自动执行的关键是,必须有一种机制,自动控制 Generator 函数的流程,接收和交还程序的执行权。回调函数可以做到这一点,Promise 对象也可以做到这一点。

以上内容摘自阮老师的es6 

感觉  thunk函数的这种自动流程管理 思想和webpack 的核心库tapable 中的异步钩子的思想一样  

 

 

  相关解决方案