Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all articles
Browse latest Browse all 14821

对阮一峰老师的thunk函数一节个人的补充理解

$
0
0

对阮一峰老师的thunk函数一节个人的补充理解

ECMAScript 6 入门

thunk函数的作用非常简单 其实就是把一个函数的 执行参数 和 回调分成两个函数(可以把函数本身也进行包装) 只要是有回调函数的 就可以用thunk进行包装

比如thunk把

fn(args, callback)--->变成--->thunk(fn)(args)(callback)

thunkify的代码

function thunkify(fn) {
	return function() {
		var args = Array.prototype.slice.call(arguments);
		var ctx = this;

		return function(done) {
			var called = false;
			args.push(function() {
				if (called) return;
				called = true;
				done.apply(null, arguments);
			});
			try {
				fn.apply(ctx, args);
			} catch (err) {
				done(err);
			}
		}
	}
}

这有什么用呢?

因为thunk 形式上把回调函数和执行函数分开,所以我们可以做到在 一个地方执行执行函数,其他地方执行回函数, 而且对于很对异步操作来说, 想想, 那我们是不分可以把这些执行函数全部放在一块执行, 他们对应的回调函数放在其他地方执行,而且只是需要确定回调函数执行的顺序和嵌套关系, 就能取得执行函数的返回顺序

用代码解释一下上面的信息

var fs = require('fs');
var readFile = thunkify(fs.readFile);

//这是执行函数集合
var f1 = readFile('./a.js');
//用户自定义的逻辑在这?
var f2 = readFile('./b.js');
var f3 = readFile('./c.js');



//这是回调函数集合

//利用嵌套控制f1 f2执行的顺序
f1(function(err, data1) {
	//还是用户定义的逻辑在这?
	f2(function(err, data2) {

		f3(function (err, data3) {

		})
	})
})


//传统写法
fs.readFile('./a.js', function(err, data1) {
	//传统用户定义的逻辑在这
	fs.readFile('./b.js', function(err, data2) {
		fs.readFile('./c.js', function(err, data3) {

		})
	})
})


可以看到把执行函数和回调函数分开以后,代码清晰了许多 但是问题来了 用户自定义的逻辑应该放在哪

首先先说一点 回调函数的一个作用就是获取数据的

那么对应thunk定义的函数来说,用户自定义的逻辑到底是放在 回调函数的集合这边还是放在 执行函数集合那边

如果用户自定义的逻辑是放在回调函数集合那边, 有两个缺点

  1. 代码逻辑里面上面来说不符合常规逻辑
  2. 回调函数里面嵌套逻辑处理太多的话,那thunk的优势就没了

那就确定了把用户自定义的逻辑放在执行函数的基本一端,回调函数只是负责获取数据,并在数据传回执行函数集合

所以现在的基本流程就是

执行函数执行-->等待回调函数传回数据-->用户对于获取的数据进行操作

等待传回数据是不是想到了 gennerator yield

所以就有了thunkify和generator的完美结合

如何结合?

把所有的执行函数放入generator函数里面,利用generator函数的yield对执行函数的流程控制 把函数执行权移出函数到对应的回调函数,获取数据后再把数据返回来

利用fs.readFile举例子

利用thunk把fs.readFile(arguments, callback) 执行的参数和回调函数分开 从而变成 执行函数放在一起 回调函数放在一起 利用yield进行连接

var fs = require('fs');
var readFile = thunkify(fs.readFile);

//发现执行参数的函数在一起
var gen = function* () {
	var data1 = yield readFile('./a.js');
	//用户获取数据后自定义写在这里
	console.log(data1.toString());
	var data2 = yield readFile('./b.js');
		//用户获取数据后自定义写在这里
	console.log(data2);
}


//写个执行函数
//发现callback在一起  而且调用的形式都一样
var g = gen();
var d1 = g.next();

//执行value 实际为执行总函数 -->回调函数
d1.value(function(err, data) {
	if (err) throw err;
	//传回数据
	var d2 = g.next(data);
	d2.value(function(err, data2) {
		if (err) throw err;
		g.next(data2);
	});
});

发现上面的g的执行形式单一

基本形式为

d.value(function(err, data) {
 	if (err) throw err;
 	g.next(data);
 })

可以利用递归写一个run函数 每个下一个都只和回调函数 callback(err ,data)有关 提取callback(err, data)

function run(fn) {

	var g = fn();
	//下一步----实际就是回调函数
	function next(err, data) {
		//把前面一个数据给传递到gen()函数里面
		var result = g.next(data);
		//判断是否结束
		if (result.done) return;
		//下一句执行回调next的时候 不断的递归
		result.value(next);
	}
	//执行第一步
	next();
}

run(gen);

Viewing all articles
Browse latest Browse all 14821

Trending Articles