了解generator 和 Promise async await 之间的关系

1,930 阅读3分钟

什么是generator? 
       generator 中文译为生成器,是es6提供解决异步编程的一种方案。以往函数的使用只有被调用和没有调用的情况,也就是说函数只有执行和未执行的状态。但generator最大的特点就是可以交出函数的执行权(即暂停执行)。说到这里,感兴趣的同学可以了解下什么是 协程。

generator的形式?
     传统的函数:   

function hello(name){
    
    return "hello " + name;
}
console.log(hello('james'));   // hello james

      generator 定义函数:

function* hello(name){
    return "hello " + name;
} 
consolo.log(hello('james));            // Object [Generator] {}
console.log(hello('james').next());     //{ value: 'hello james', done: true }

以上可以看出generator定义函数与普通函数相比,致多处了 * 号。通过generator.next()单步执行,next返回一个对象包括value和done,value为当前程序的计算结果,而done则表示程序是否执行完成。
上文说到有个暂停执行,yield表达式是暂停执行的标记,next是恢复执行。

const hello = function* (data){    
            const {name1, name2} = {...data};    
            yield "hello "+name1;    
            yield "hello "+name2;    
            return 'success';
}

const obj = hello({'name1':'aaa','name2':'bbb'});
console.log(obj.next());   //{ value: 'hello aaa', done: false }
console.log(obj.next());   //{ value: 'hello bbb', done: false }
console.log(obj.next());   //{ value: 'success', done: true }

什么是 Promise ?
Promise是es6一种异步编程的解决方案,Promise 本身是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。下面上手一个列子明白Promise的使用:

new Promise((reject, resolve) => {
    setTimeout(function(){
        resolve('seccess');
    }, 2000);
}).then((data) => {
    console.log(data);   //seccess
});

Promise构造函数接受一个函数作为参数,resolve和reject 表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。在reject或者resolve之后then执行一个函数来后去最后状态的值。这样就能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。

generator生成器取代链式调用

let asyncfunc = (a, b)=>{    
    return new Promise((resolve, reject) => {        
            setTimeout(()=>{            
                resolve(a+b);        
            }, 1000);    
        });} 

let g = function* (){    
    let res = yield asyncfunc(2,2);    
    return res;
}

let item = g();
let p = item.next().value;
p.then((sum)=>{    
    console.log(item.next(sum));
});

下面我们使用委托模式实现生成器自动迭代,使用递归消除next的重复调用。

let asyncfunc = (a, b)=>{    
        return new Promise((resolve, reject) => {        
            setTimeout(()=>{            
                resolve(a+b);        
            }, 1000);    
        });
} 

let g = function* (){    
        let res = yield asyncfunc(2,2);    
        return res;
}

function Co(item){    
        return new Promise((res, rej) =>{        
            let next = (data) => {            
                let {value, done } = item.next(data);            
                if(done){                
                    res(value);            
                }else{                
                    value.then((value) =>{                    
                        next(value);                
                    }, rej);            
                }        
        };        
        next();    
        });
}

Co(g()).then((val)=>{    
        console.log(val);
});

async+await
Generator+Promise给嵌套的异步任务提供了一个非常便捷的解决方案,这是异步任务非常典型的一种场景。有了生成器的函数执行新模式的出现,让标准进一步跟进规范这一场景的解决方案,在ES2017(ES8)标准引入async函数,使得异步操作变得更加方便,而实际上async函数就是Generator函数的语法糖。直接看基于async函数如何改写示例

let asyncfunc = (a, b)=>{    
        return new Promise((resolve, reject) => {        
            setTimeout(()=>{            
                resolve(a+b);        
            }, 1000);    
        });
} 

let g = async function(){    
        let res = await asyncfunc(2,2);    
        return res;
}

g().then((data)=>{    
        console.log(data);
});

async将异步链式嵌套任务完全转化成了按照代码编写的先后顺序的同步执行任务,这里所指代的同步任务是指由async+await控制的内部异步任务,async函数本身是一个异步任务,它执行返回的是一个Promise对象用来处理异步链式任务的最后回调处理。