处理异步de发展史

1.处理异步的发展史

a.远古时代

我们在编写express后台,经常要有许多异步IO的处理。在远古时代,我们都是用chunk函数处理,也就是我们最熟悉的那种默认第一个参数是 error 的函数。我们来模拟一个Mongo数据库的操作,感受一下。

mongoDb.open(function(err, db){
	if(!err){
		db.collection("users", function(err, collection){
			if(!err){
				let person = {name: "yika", age: 20};
				collection.insert(person, function(err, result){
					if(!err){
						console.log(result);
					}
				});
			}
		})
	}
});

这个也就是被我们所诟病的 callback hell ,一堆横向金字塔,如果将回调拆分成函数,则会变得非常支离破碎。为了防止到恶心到大家,我甚至没有写关于错误的处理,正常来说,每一个异步的操作都需要都它的 error 进行相应的显示或处理的。

b.Promise时代

后来进入了好一点的时代就是Promise,我们也可以称作链式操作。关于Promise,我也是之前有专门写过一系列的博文,有兴趣可以回头翻一下。这里来看看,将以上改写之后的状况。

let co = require("co");
co(function *(){
	let db, collection, result; 
	let person = {name: "yika"};
	try{
		db = yield mongoDb.open();
		collection = yield db.collection("users");
		result = yield collection.insert(person);
	}catch(e){
		console.error(e.message);
	}
	console.log(result);
});

我们可以看到,我们将金字塔已经平铺成一条线状结构了。相比之前恶心难以维护的chunk函数,变成了promise函数,并且错误的处理也变得十分优雅。但是我们仍然不可忽视某些问题,例如我们必须忍受各个逻辑被一个又一个的 then() 包裹起来,每一个函数都有其独立的作用域,如果为了共享某个数据就必须挂在最外层,最重要的还是,它与我们熟悉的同步编程仍然有差别。

c.Generator时代

TJ大神,借着ES6的Generator迭代器,最早实现了异步编程同步化的功能,也就是最为我们所熟知的 co 库。我们通过 co(function *(){}) 可以使函数内部通过迭代器来控制。而 co 在这里则是充当了启动器的角色。关于Generator和co我在之前的博文也同样说过。

async function insertData(person){
	let db, collection, result; 
	try{
		db = await mongoDb.open();
		collection = await db.collection("users");
		result = await collection.insert(person);
	}catch(e){
		console.error(e.message);
	}
	console.log(result);
} 
insertData({name: "yika"});

我们已经非常接近同步编程了,在co包裹的函数内部,只有一个异步执行完毕,才会继续执行下面的代码。并且错误的处理也是通过 try and catch 进行实现的。不过我们不得不承认的是,迭代器终究不是为异步而存在的。里面的 yield* 的语义也并不代表的就是异步函数标志。并且迭代器是需要co去驱动的,它和我们想象中的函数多少有一点点不同。

d.async/await时代

我们关注到ES7的async/await,才发现这才是我们想要的!我们将上面的代码小小改写一下。


// webpack.config.js
// 省略上面的文件输入输出的配置,直接看模块加载器的配置
module: {
	loaders: [
		{
			test: /\.js$/,
			exclude: /(node_modules|bower_components)/,
			loader: "babel",
			query: {
			  presets: ['es2015', 'stage-3']
			}
		},
	]
}

我们可以看到 inserData 是一个真正的函数,是我们可以直接去调用而无需启动器驱动的。当然内部我们也可以感受到处理 yield 变成了 await 以外,并没有很大区别。async/await,更符合我们异步编程的语义。

廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

w

連結到 %s