ES7的async和await,目前最为简略的异步解决方案

299 阅读4分钟

随着js的发展,在解决回调地狱问题的方案上,解决方案逐渐更新,有promise、generator和现在的async都是比较常见的; 1.Promise 这个解决方案就是把异步用同步的方式写出来,一步一步的.then()方法,前一个.then()执行完之后,继续执行下一个.then();

function timeout (time) {
	return new Promise((res, rej)=>{
		setTimeout(()=>{
			res('this is a returnDate');
		},time)
	})
};

time(5000)
.then((data)=>{console.log(data)})
.then(()=>{console.log('end')})
...

这就是promise的异步解决方案看起来还是很简单的,其实只是以你为逻辑简单;

2.Generator Generator函数是个异步管理器,这里要提到一个协程的概念,先不用管协程是什么鬼,先来介绍一下yield命令,它是Generator函数中的分水岭,在函数内部起执行权转义的功能,什么是执行权?好吧,这就是在Generator函数内部遇到yield命令,那么就不往下执行了,就把执行权交出来给别的函数,等别的函数执行之后返回结果了,在返还执行权,继续向下执行Generator函数的语句;

function generator () {
	// 代码A;
	var f = yield readFile(fileA);
	// 代码A;
}

以上代码中,当函数执行的时候分一下步骤: a.开始执行代码A; b.代码A执行到一半,遇到yield命令,暂停执行,开始执行,执行权交给yield命令后面的函数; c.当yield函数执行完之后,交还执行权; d.继续执行yield命令下面的代码A; 在这个过程中,代码段A被分成两段执行,代码段A叫做协程A,readFile()函数叫做协程B,协程A是异步的; 再来举个例子:

function* gen(x){
	var y = yield x+2;
	return y
}

var g = gen(1);
g.next() // { value:3, done:false }
g.next() // { value:undefined, done:ture }

a.调用Generator函数并不会像其他普通函数一样返回函数值,它会返回一个内部指针——g; b.调用内部指针中的next方法,会移动指针分配执行权去手动执行函数;返回结果会是一个对象,value字段对应运算值,done字段是布尔类型,说明函数是否执行完; c.上面代码中,第一次调用next方法,函数执行到x+2,值返回给value属性;第二次调用next方法,执行代码是return y,这时y并没有值,只是定义了,所以第二次vlaue的值是undefined; d.当然next方法也是可以传值的,如下:

var g = gen(1);
g.next() // { value:3, done:false }
g.next(2) // { value:2, done:ture }

这时第二个next方法带有参数2,这个参数会传到Generator函数内部,作为上个协程任务的返回结果被函数体内的变量y接收,因此value的值是2;

对了这里在补一句,从上可以看出Generator函数是需要手动执行的,那么有没有可以让其自执行的办法呢,有~ co模块小工具可以让Generator函数自执行,只要把其当参数传进co中;

var co = require('co');
co(gen);

3.async函数 这个es7草案,说实话也是最简单的异步解决方案,问其原理,其实是Generator函数的语法糖; a:

function timeout (time) {
	return new Promise((res, rej)=>{
		setTimeout(()=>{
			res();
		},time)
	})
}

let start = async function () {
	console.log('start');
	let data = await timeout(5000);
	console.log('end');
}

执行结果是: 先打印出start,然后转为timeout函数执行,执行时间为5s,之后返回打印出end; 是不是跟Generator函数很相似,awaityield命令作用一样~

b.当然了,也是可以获取到返回值的:

function timeout (time) {
	return new Promise((res, rej)=>{
		setTimeout(()=>{
			res('this is a returnDate');
		},time)
	})
}

let start = async function () {
	console.log('start');
	let data = await timeout(5000);
	console.log(data);
}

这样,5s后打印出来的就是this is a returnDate;

c.当然,也可以捕获错误:

function timeout (time) {
	return new Promise((res, rej)=>{
		setTimeout(()=>{
			rej('this is an err');
		},time)
	})
}

let start = async function () {
	console.log('start');
	try {
		let data = await timeout(5000);
		console.log(data);
	} catch (err) {
		console.log(err);
	}
	
}

这里timeout函数返回一个错误,那么被try...catch...语句中的catch捕获,则不执行try中的代码,直接执行catch中的代码,打印出错误;

d.当然了,这里也是可以循环多执行await的

function timeout (time) {
	return new Promise((res, rej)=>{
		setTimeout(()=>{
			rej('this is an err');
		},time)
	})
}

let start = async function () {
	for(let i=0; i<=10; i++) {
		console.log(`这是第${i}次等待`);
		await timeout(5000);
	}
}

这里指出非常重要的一点,await命令必须要在async函数的上下文中,也就是说当await命令所在的环境中this的指向不是async的时候,会报错! 所以这里要用for循环,for...of循环,不能用map,forEach方法等;