【javaScript】深入理解Generator与async/await

231 阅读4分钟

发展由来

  • Promise写起来代码较为冗余 总是要return new Promise和then拉链
  • Generator手动控制在何处等待何处执行(一般都是等待异步操作)
  • async/await是自动Generator

yield与neet参数

重点:yield作为分界线,如果有 var a = yield exp,还没执行赋值就返回exp了,而exp会根据下一次next的参数改变

遍历器对象的next方法的运行逻辑如下:

(1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。

(3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。

function * f() {
    for( let i =0; true; i++){
        let reset = yield i;
        if(reset){
            i = -1;
        }
    }
}

let g = f();
console.log(g.next());
console.log(g.next());
console.log(g.next(true)); //yield返回值被设置为true,导致reset被赋值为true

输出结果:

{ value: 0, done: false }
{ value: 1, done: false }
{ value: 0, done: false }

可以看到最后的一步,我们使用next传入的true替代了i的值,最后导致i= -1 + 1 = 0.

我们再看一个例子:

function * f2(x){
    var y = 2 * ( yield ( x + 1));
    var z = yield (y / 3);
    return (x + y + z);
}

var r1= f2(5);
console.log(r1.next());
console.log(r1.next());
console.log(r1.next());

var r2= f2(5);
console.log(r2.next());
console.log(r2.next(12));
console.log(r2.next(13));

输出结果:

{ value: 6, done: false }
{ value: NaN, done: false }
{ value: NaN, done: true }

{ value: 6, done: false }
{ value: 8, done: false }
{ value: 42, done: true }

注意,yield句本身没有返回值,或者说总是返回undefined,等到再一次next时,next的参数只是重新赋值yield后面的表达式的值,从而忽略yield。也就是说,如果next不传值的话,yield本身是没有返回值的,所以我们会得到NaN。但是如果next传入特定的值,则该值会替换该yield,成为真正的返回值。

async/await

简单的说async函数就相当于自执行的Generator函数,相当于自带一个状态机,在await的部分等待返回, 返回后自动执行下一步。而且相较于Promise,async的优越性就是把每次异步返回的结果从then中拿到最外层的方法中,不需要链式调用,只要用同步的写法就可以了。更加直观而且,更适合处理并发调用的问题。但是async必须以一个Promise对象开始 ,所以async通常是和Promise结合使用的

async函数用法

上面说了async不过是Generator函数的语法糖,那为什么要取这个名字呢?自然是有理由的。 async是“异步”,而await是async wait的简写,即异步等待。所以应该很好理解async用于声明一个function是异步的,await用于等待一个异步方法执行完成

下面来看一个例子理解async命令的作用

async function test() {
  return "async 有什么用?";
}
const result = test();
console.log(result) 

输出:
Promise { 'async 有什么用?' }
可以看到,输出的是一个Promise对象**!**

所以,async函数返回的是一个Promise对象,如果直接return 一个直接量,async会把这个直接量通过PromIse.resolve()封装成Promise对象

注意点
一般来说,都认为await是在等待一个async函数完成,确切的说等待的是一个表示式,这个表达式的计算结果是Promise对象或者是其他值(没有限定是什么)

即await后面不仅可以接Promise,还可以接普通函数或者直接量。

同时,我们可以把async理解为一个运算符,用于组成表达式,表达式的结果取决于它等到的东西

  • 等到非Promise对象 表达式结果为它等到的东西
  • 等到Promise对象 await就会阻塞后面的代码,等待Promise对象resolve,取得resolve的值,作为表达式的结果
//回调写法
function fun1(value) {
	retrun	new Promise((resolve,reject)=> {
        setTimeout(function(callback){
      		resolve(value++)
    	},2000);
	}) 
}

fun1(0).then((value)=> {
    return 	new Promise((resolve,reject)=> {
        setTimeout(function(callback){
      		resolve(value++)
    	},2000);
}).then((value)=> {
	return 	new Promise((resolve,reject)=> {
        setTimeout(function(callback){
      		resolve(value++)
    	},2000);
}).then(value)=>{
    console.log(value) 
}    // 4

//async函数写法
function fun1(value) {
	retrun	new Promise((resolve,reject)=> {
        setTimeout(function(callback){
      		resolve(value++)
    	},2000);
	}) 
} //返回promise实例

async function asy() {
let v = 0
    v = await fun1(v) //等待promise的resolve值作为=右边的结果
    v = await fun1(v)
    v = await fun1(v)
    console.log(v)
}  
asy() //4