阅读 203

Generator和async/await

前言

纯属个人学习过程中的笔记,方便后续的复习,如果有错误或者更好的方法,希望看到的大佬不吝赐教

Generator

学习Generator之前,需要简单了解一下迭代器

迭代器

有next方法,执行返回结果对象,返回的对象有done和value两个属性
下面用代码看一下迭代器是个什么东西

        function createIterator(list) {
            let i = 0;
            return {
                next() {
                    let done = i >= list.length;
                    let value = !done ? list[i++] : undefined;
                    return {
                        done,
                        value,
                    };
                },
            };
        }
        let iterator = createIterator([1, 2, 3]);
        console.log(iterator.next()); //{ done: false, value: 1 }
        console.log(iterator.next()); //{ done: false, value: 2 }
        console.log(iterator.next()); //{ done: false, value: 3 }
        console.log(iterator.next()); //{ done: true, value: undefined }
复制代码

Generator(生成器)

  • ES6异步编程解决方案
  • 通过function* 声明
  • 返回符合可迭代协议和迭代协议的生成器对象
  • 在执行时能暂停,又能从暂停处继续执行

yield

  • 只能出现在Generator函数中
  • 用来暂停和恢复生成器函数

next

  • 遇到yeild暂停,将紧跟yeild表达式的值作为返回对象的value
  • 没有yeild,执行到return ,将return的值作为返回对象的value
  • 没有return 返回对象的value是undefined
  • next可以携带一个参数,可以作为上一个yeild(暂停的那个)表达式的返回值
        function* generator() {
            let first = yield 1;
            let second = yield first + 2;
            let thrid = yield second + 3;
            return 1;
        }
        let g = generator();
        console.log(g.next()); // {value: 1, done: false}
        console.log(g.next(2)); // {value: 4, done: false}
        console.log(g.next(3)); // {value: 6, done: false}
        console.log(g.next()); // {value: 1, done: true} //如果没有return ,value则是undefined
复制代码

yeild*

  • 是生成器函数/可迭代对象
  • 可以委托其他生成器
  • 用来复用生成器

通俗的说,就是yeild* 可以在生成器中调用另外一个生成器,还是看代码

        function* generator1() {
            yield 1;
        }

        function* generator2() {
            yield 100;
            yield* generator1();
            yield 200;
        }
        let g2 = generator2();
        console.log(g2.next()); //{value:100,done:false}
        console.log(g2.next()); //{value:1,done:false}
        console.log(g2.next()); //{value:200,done:false}
        console.log(g2.next()); //{value:undefined,done:true}
复制代码

return

  • return,可以带一个参数,结束生成器的执行,返回对象的value为传的参数,不传则为undefined
        function* generator() {
            yield 100;
            yield 200;
        }
        let g = generator();
        console.log(g.next()); //{value:100,done:false}
        console.log(g.return(123)); //{value:1,done:true}
        console.log(g.next()); //{value:undefined,done:true }
复制代码

throw

  • 可以让生成器内部抛出错误
        function* generator() {
            let first = yield 1;
            let second;
            try {
                second = yield first + 2;
            } catch (error) {
                second = 6;
            }
            yield second + 3;
        }
        let g = generator();
        console.log(g.next()); //{value:1,done:false}
        console.log(g.next(1)); //{value:3,done:false}
        //可以看出由于有trycatch,抛出的错误被捕获到,所以走到了catch里面,second为6
        console.log(g.throw(new Error("error"))); //{value: 9, done: false}
        console.log(g.next()); //{value:undefined,done:true}
复制代码

协程

Generator是怎么实现的呢,需要了解一下协程
协程:可以暂停执行(暂停的表达式称之为暂停点),也可以从挂点恢复执行(保留其原始参数和局部变量)
一个线程存在多个协程,但是只能同时执行一个协程
Generator就是协程在ES6上面的表现
yield就是挂起协程,next则是唤醒协程

Generator函数的应用

用setTimeout 模拟异步 //这种方法虽然不会造成回调地狱,但是代码耦合度比较高

        function* generator() {
            function test(num) {
                setTimeout(() => {
                    console.log(num);
                    g.next(num);
                }, 2000);
            }
            let r1 = yield test(1);
            console.log(r1, "r1");
            let r2 = yield test(2);
            console.log(r2, "r2");
        }
        let g = generator();
        g.next();
复制代码

thunk

为了解决上述代码的耦合问题,需要使用thunk函数,可以自动执行Generator函数
函数比较绕,先上完整代码,然后慢慢理解

const fs = require("fs");

function thunk(fn) {
    return function(...args) {
        return function(callback) {
            return fn.call(this, ...args, callback);
        };
    };
}
let readFileThunk = thunk(fs.readFile);

function* generator() {
    let r1 = yield readFileThunk("./test1.json");
    console.log(r1, "r1");
    let r2 = yield readFileThunk("./test2.json");
    console.log(r2, "r2");
    let r3 = yield readFileThunk("./test3.json");
    console.log(r3, "r3");
}

function run(fn) {
    let gen = fn();

    function go(data, err) {
        console.log(data, err, "data123");
        let result = gen.next(data);
        if (result.done) return;
        result.value(go);
    }
    go();
}
run(generator);
复制代码

readFileThunk

function(...args) {
        return function(callback) {
            return fs.readFile.call(this, ...args, callback);
        };
    };
复制代码

result.value

function(callback) {
            return fs.readFile.call(this, ...args, callback);
        };
复制代码

run的参数是生成器,运行之后拿到迭代器gen,然后执行next方法,如果返回的对象的value是false,则将go作为result.value的参数,result,value执行的时候,意味着fs.readFile执行,传入的callback(go)将作为fs.readFile的参数(回调函数)执行,然后又再一次回到了go函数内部,反之返回对象的value是true 的时候,则结束执行

async/await

async/await是Generator和promise的语法糖

async

  • 返回值:返回一个promise对象,return的值是promise resolved时候的value,Throw的值是promise rejected时候的reason

      async function test1() {
        return 1;
      }
      let p1 = test1(); //一个promise
      p1.then((res) => {
        console.log(res); //1
      }); 

      async function test2() {
        throw new Error("error");
      }
      let p2 = test2();
      p2.catch((err) => {
        console.log(err); //Error: error
      });

复制代码

await

  • 只能出现在async函数内部或者最外层
  • 等待一个promise对象
  • await的promise对象状态为rejected的时候,后续执行中断
  • 如果希望执行,可以在后面加一个catch或者在trycatch中执行
      async function test() {
        console.log(1);
        await Promise.resolve().then(() => {
          console.log(2);
        });
        console.log(3);
      }
      test(); // 1 2 3

      async function test() {
        console.log(1);
        await Promise.reject();
        console.log(3);
      }
      test(); // 1
      async function test() {
        console.log(1);
        await Promise.reject().catch((err) => {
          console.log(2);
        });
        console.log(3);
      }
      test(); // 1 2 3
      async function test() {
        console.log(1);
        try {
          await Promise.reject();
        } catch (error) {
          console.log(2);
        }
        console.log(3);
      }
      test(); // 1 2 3
复制代码

async函数使用

        async function test() {
            console.log(1);
            await new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(2);
                    resolve();
                }, 3000);
            });
            console.log(3);
            await new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(4);
                    resolve();
                }, 2000);
            });
        }
        test(); //1 2 3 4
复制代码
文章分类
前端
文章标签