ES6之generator,async,await

引言

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

async 是一个通过异步执行并隐式返回 Promise 作为结果的函数(MDN)。

generator

Generator 语法,需要了解function* 、yield、next三个基本概念。
generator函数产生一个迭代器,迭代器对外暴露一个next方法,通过这个方法,获得迭代器的yield的值,同时获得done,用来表示下一次next是不是能获取到值。
1.function* 用来声明一个函数是生成器函数,它比普通的函数声明多了一个,的位置比较随意可以挨着 function 关键字,也可以挨着函数名

2.yield 产出的意思,这个关键字只能出现在生成器函数体内,但是生成器中也可以没有yield 关键字,函数遇到 yield 的时候会暂停,并把 yield 后面的表达式结果抛出去

3.next作用是将代码的控制权交还给生成器函数

生成器的使用

代码示例

function* fun(n) {
    let a = yield n * 2;
    let b = yield a + 5;
    let c = yield b / 2;
    return c;
}
let funGen = fun(12);
let resultObj1 = funGen.next();//{value: 24, done: false}
let resultObj2 = funGen.next(resultObj1.value);//{value: 29, done: false}
let resultObj3 = funGen.next(resultObj2.value);//{value: 14.5, done: false}
let resultObj4 = funGen.next(resultObj3.value);//{value: 14.5, done: true}
console.log(resultObj4.value); //14.5

过程分析

1.生成器就是在普通函数名称与function关键字之间的任意位置标记一个“*”表示该函数是一个生成器。

2.生成器执行会返回一个Generator对象,也可以视该对象为一个Iterator,因为该对象同样可以被迭代。

3.生成器执行生成一个Generator对象的时候迭代器内部代码不会执行,而是需要通过对象调用next()方法才会执行内部代码。

4.Generator对象调用next()方法基于yield关键字迭代执行,next()方法第一次执行是从头开始执行到第一个yield的右侧代码,yield左侧代码会等到下一个next()调用才会执行。当所有yield关键被迭代完成以后,最后一个next()方法返回的对象中done属性值为true,表示该Generator被迭代到最末尾处。

5.被yield截断的表达式除了作为阻断代码执行的作用以外,yield关键字同时充当了表达式右侧代码的return功能,将右侧代码执行结果作为当前next()方法的返回值;yield关键还充当了左侧代码被next()调用时接收参数的功能。(yield关键之应该很容易理解,它的功能就是截断程序执行,并且通过返回值和接收参数的方式连接被截断的程序)

6.yield作用的return特性其返回值最后会被next()方法返回的对象中的value属性获取。

generator函数和普通函数对比.png

生成器 + Promise

使用生成器取代Promise的链式调用

let fs = require('fs');

function readFile(path){
    return new Promise((res,rej) => {
        fs.readFile(path, 'utf-8', (err,data) => {
            if(data){
                res(data);
            }else{
                rej(err);
            }
        });
    });
}

function *read(){
    let val1 = yield readFile('./data/number.txt');
    let val2 = yield readFile(val1);
    let val3 = yield readFile(val2);
    return val3;
};
let oG = read();
let {value, done} = oG.next();
value.then((val) => {
    let {value,done} = oG.next(val);
    value.then((val) => {
        let {value,done} = oG.next(val);
        value.then((val) => {
            console.log(val);
        });
    });
});

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

let fs = require('fs');

function readFile(path){
    return new Promise((res,rej) => {
        fs.readFile(path, 'utf-8', (err,data) => {
            if(data){
                res(data);
            }else{
                rej(err);
            }
        });
    });
}

function *read(){
    let val1 = yield readFile('./data/number.txt');
    let val2 = yield readFile(val1);
    let val3 = yield readFile(val2);
    return val3;
};

function Co(oIt){ //生成器迭代委托
    return new Promise((res,rej) =>{
        let next = (data) => {
            let {value, done} = oIt.next(data);
            if(done){
                res(value);//当迭代器到达最末尾时,将生成器的返回值传递给Promise的受理回调执行回调任务
            }else{
                value.then((val) => {
                    next(val);//将上一个生成器返回值传递给下一个生成器的迭代方法next(这是个递归操作)
                },rej);//在生成器迭代过程中如果发生异常会调用rej处理
            }
        }
        next();//生成器第一次执行不需要参数
    });
}

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

async+await

使用async函数如何改写上节中的示例:

let fs = require('fs');

function readFile(path){
    return new Promise((res,rej) => {
        fs.readFile(path, 'utf-8', (err,data) => {
            if(data){
                res(data);
            }else{
                rej(err);
            }
        });
    });
}
async function read(){
    let val1 = await readFile('./data/number.txt');
    let val2 = await readFile(val1);
    let val3 = await readFile(val2);
    return val3;
};
read().then((val) => {
    console.log(val); //99
})

从表面上看async将异步链式嵌套任务完全转化成了按照代码编写的先后顺序的同步执行任务,这里所指代的同步任务是指由async+await控制的内部异步任务,async函数本身是一个异步任务,它执行返回的是一个Promise对象用来处理异步链式任务的最后回调处理。
从async本身来说就是将内部代码交给一个Promise作为excutor函数,然后将return返回值交给回调函数,Promise通过then注册的回调任务作为微任务(异步)处理。简单的说async函数返回一个Promise对象,并将返回值作为回调任务的参数,这里的底层实现可以直接参数Co理解,也可以称为内置执行器,转码工具中的函数名称是asyncReadFile()。接着来看await在async函数内的作用:


yield和await关键字的作用.png

async函数的使用

// 函数声明
async function foo(){}
// 函数表达式
let foo = async function(){}
// 对象方法
let obj = {async foo(){}};
obj.foo().then(...);
// class的方法
class Storage{
    constructor(){
        this.cachePromise = caches.open('avatars');
    }
    async getAvatar(name){
        const cache = await this.cachePromise;
        return cache.match(`/avatars/${name}.jpg`);
    }
}
const storage = new Storage();
storage.getAvatar('jake').then(...);
//箭头函数
const foo = async () => {};

参考资料

https://www.cnblogs.com/ZheOneAndOnly/p/11411971.html
https://blog.csdn.net/zjscy666/article/details/95365911?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
https://www.jianshu.com/p/6055bd421ca4

你可能感兴趣的:(ES6之generator,async,await)