异步方案进化过程: callback ---> promise ---> generator + co ---> async + await 本文以nodejs中最常用的文件异步读取操作readFile为例,来说明各个方案下的实现方式。
前提
假设我们现在有两个文件1.txt和2.txt,1.txt的内容是'./2.txt'(2的路径),2.txt的内容是'异步,又见异步'。
需求1
拿到1的内容作为路径去读取2的内容打印出来。
需求2
同时读取两个文件,在两个文件都读取完成时,将文件中的内容合并。
callback
需求1实现
fs.readFileSync('./1.txt', 'utf8', function(err, data) { if(err) { throw err; } fs.readFileSync(data, 'utf8', function(err, data) { if(err) { throw err; } console.log(data); })})复制代码
此时我们很容易利用了回调嵌套,嵌套多了就容易累积出回调地狱。
需求2实现
这个需求实现我们先需要了解一个lodash的_after函数
_after函数
_after函数返回一个函数,在这个函数调用次数达到一定限度时,调用回调函数。下面写一个自己的after函数:
function after(times, callback) { return function() { if(--times === 0) { callback(); } };}let sleep = after(3, function() { console.log('睡饱了');});sleep();sleep();sleep();//打印出睡饱了复制代码
写一个afterRead函数来得到一个read函数,如下:
function afterRead(times, callbacks) { let arr = []; return function(path) { fs.readFile(path, 'utf8', function(err, data) { if(err) { throw err; } arr.push(data); if(--times === 0) { callbacks(arr); } }); }}let read = afterRead(2, function(dataArr) { console.log(dataArr);});read('./1.txt');read('./2.txt');//读完第二个文件打印出['./2.txt', '异步,又见异步']复制代码
Promise
需求1实现
function read(path) { return new Promise(function(resolve, reject) { fs.readFile(path, 'utf8', function(err, data) { if(err) { reject(err); } resolve(data); }); });}read('./1.txt').then(function(data) { return read(data);//将产生的promise返回出去进行下一步处理}, function(err) { console.log(err);}).then(function(data) { console.log(data);});复制代码
需求2实现
使用Promise.all(),能够方便进行处理,并且返回的数据是按照请求发起的顺序存放
Promise.all([read('./1.txt'), read('./2.txt')]).then(function(data) { console.log(data);});复制代码
Generator + co
Generator是一个能够返回迭代器的函数,需要在function关键字和名字之间加上*以标识是个Generator,需要配合yield使用,方法每次执行到yield就会暂停,等待返回的迭代器调用next()方法继续往下执行。
next方法
返回一个包含value和done属性的对象,value表示值,done布尔类型,表示迭代是否完成。
co
co库可以自动的将generator进行迭代,同时每次迭代都会返回一个promise
模仿co库的功能
function co(it) { let task = it();//taskDef生成一个任务迭代器 return new Promise(function(resolve, reject) { function step(data) { let { done, value} = task.next(data); if(!done) { value.then(function(data) { step(data); }, reject); } else { resolve(value); } } step(); }); }复制代码
需求1实现
function *readTask() { let path = yield read('./1.txt'); let content = yield read(path); return content;}co(readTask).then(function(data) { console.log(data); });复制代码
终极方案 asyn await
async和await(语法糖) === co + generator
用async 来修饰函数,aysnc需要配await,await只能promise
需求1实现
async function readTask() { try { let path = await read('./1.txt'); let content = await read(path); return content; } catch (error) { throw err; }}readTask().then(function(data){ console.log(data);});复制代码