generator函数和async/await

generator函数与普通函数的区别:多个*,并且只有在generator函数中才能使用yield,yield相当于generator函数执行的中途暂停点,可以通过.next继续执行下一个,.next方法返回一个对象,{value: any, done: Boolean}

  • value: 暂停点后面接的值,即yield后面的值
  • done:generator函数是否已经结束,是 true,否 false
js 复制代码
function* gen() {
  yield 1;
  yield 2;
  yield 3;
  // return 8;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 3, done: false }
console.log(g.next()); // { value: undefined, done: true } | { value: 8, done: true } 最后一个value值取决于generator函数有没有返回值

一 、generator

1. yield后面接函数

js 复制代码
// 执行到了对应的yield时,会立刻执行此函数,并且函数的返回值会被赋给yield暂停点对象value
function fn(num) {
  return num * 2;
}

function* gen() {
  yield fn(1);
  yield fn(2);
  return 10;
}
const g = gen();
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 4, done: false }
console.log(g.next()); // { value: 10, done: true }

2. yield后面接Promise

js 复制代码
function fn(num) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(num * 2);
    }, 1000);
  });
}

function* gen() {
  yield fn(1);
  yield fn(2);
  return 10;
}

const g = gen();
// console.log(g.next()); // { value: Promise { <pending> }, done: false }
// console.log(g.next()); // { value: Promise { <pending> }, done: false }
// console.log(g.next()); // { value: 10, done: true }

// 若想要获取promise的结果,可以使用.then
g.next().value.then((val1) => {
  console.log(val1);
  g.next().value.then((val2) => {
    console.log(val2);
  });
});

3. next函数传参

generator函数可以用next方法来传参,并且可以通过yield来接收这个入参,注意以下两点:

  1. 第一次next传参是没有作用的,只有从第二次开始next传参才有用
  2. next传值时,顺序为:先执行右边的yield,然后左边接收的是下一次next的传参
js 复制代码
function* gen() {
  const test2 = yield 1;
  console.log(test2);
  const test3 = yield 2;
  console.log(test3);
  return 10;
}

const g = gen();
console.log(g.next());
// { value: 1, done: false }

console.log(g.next("test2"));
// test2
// { value: 2, done: false }

console.log(g.next("test3"));
// test3
// { value: 10, done: true }

4. Promise & next传参

js 复制代码
function fn(num) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(num * 2);
    }, 1000);
  });
}

function* gen() {
  const test2 = yield fn(1);
  console.log(test2); // 2
  const test3 = yield fn(test2);
  console.log(test3); // 4
  const test4 = yield fn(test3);
  console.log(test4); // 8
  return test4;
}

const g = gen();
g.next().value.then((test2) => {
  console.log(test2); // 2
  g.next(test2).value.then((test3) => {
    console.log(test3); // 4
    g.next(test3).value.then((test4) => {
      console.log(test4); // 8
      g.next(test4);
    });
  });
});

二、async/await

generator函数的Promise & next传参,类似async/await,区别在于:

  1. gen函数的执行返回值不是Promise,asyncFn执行(即asyncFn())返回值是Promise;
  2. gen函数需要执行相应的操作,才能等同async Fn的排队效果
  3. gen函数执行的操作是不完善的,因为并不确定有多少yield,不确定会嵌套几次

针对这种情况,可以通过高阶函数(HOC)封装:

高阶函数: 参数是函数,返回值也可以是函数

js 复制代码
function fn(nums) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(nums * 2);
    }, 1000);
  });
}

function* gen() {
  const num1 = yield fn(1);
  const num2 = yield fn(num1);
  const num3 = yield fn(num2);
  return num3;
}

function generatorToAsync(genFn) {
  return function () {
    return new Promise((resolve, reject) => {
      const g = genFn();
      g.next().value.then((num1) => {
        g.next(num1).value.then((num2) => {
          g.next(num2).value.then((num3) => {
            resolve(g.next(num3).value);
          });
        });
      });
    });
  };
}

const asyncFn = generatorToAsync(gen); // 返回Promise
asyncFn().then((val) => console.log(val)); // 3s后 打印8
js 复制代码
// 以上完成了async/await基础功能,等同于:
async function asyncFn() {
  const num1 = await fn(1);
  const num2 = await fn(num1);
  const num3 = await fn(num2);
  return num3;
}
asyncFn().then((res) => console.log(res));

但是上述的async await数量是固定的,对于不固定数量的await,做出以下改造:

js 复制代码
function fn(nums) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // resolve(nums * 2);
      // reject("err");
    }, 1000);
    throw "err";
  });
}

function* gen() {
  const num1 = yield fn(1);
  const num2 = yield fn(num1);
  const num3 = yield fn(num2);
  const num4 = yield fn(num3);
  const num5 = yield fn(num4);
  return num5;
}

function generatorToAsync(genFn) {
  return function () {
    return new Promise((resolve, reject) => {
      const g = genFn.apply(this, arguments); // genFn可能存在入参
      function goNext(key, nextArg) {
        let yieldValue;
        try {
          yieldValue = g[key](nextArg);
          const { value, done } = yieldValue;
          // value有可能是:常量,Promise,Promise有可能是成功或者失败
          if (value instanceof Promise) {
            if (done) {
              // 如果done为true,说明走完了,进行resolve(value)
              resolve(value);
            } else {
              // 如果done为false,说明没走完,还得继续走
              value.then(
                (res) => goNext("next", res),
                (err) => goNext("throw", err) // yield.throw()方法‌允许在Generator函数内部抛出错误,并且这个错误可以被外部的catch语句捕获
              );
            }
          } else {
            resolve(value);
          }
        } catch (error) {
          // 此处捕获yield.throw()抛出的异常
          reject(error);
        }
      }
      goNext("next");
    });
  };
}

const asyncFn = generatorToAsync(gen); // 返回Promise
asyncFn().then(
  (val) => console.log(val),
  (err) => console.log(err)
); // 3s后 打印8
相关推荐
理想不理想v19 分钟前
vue经典前端面试题
前端·javascript·vue.js
不收藏找不到我20 分钟前
浏览器交互事件汇总
前端·交互
小阮的学习笔记33 分钟前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
YBN娜34 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=34 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck38 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。1 小时前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼1 小时前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript