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
相关推荐
gnip7 分钟前
SSE技术介绍
前端·javascript
yinke小琪22 分钟前
JavaScript DOM节点操作(增删改)常用方法
前端·javascript
枣把儿26 分钟前
Vercel 收购 NuxtLabs!Nuxt UI Pro 即将免费!
前端·vue.js·nuxt.js
望获linux27 分钟前
【Linux基础知识系列】第四十三篇 - 基础正则表达式与 grep/sed
linux·运维·服务器·开发语言·前端·操作系统·嵌入式软件
爱编程的喵29 分钟前
从XMLHttpRequest到Fetch:前端异步请求的演进之路
前端·javascript
喜欢吃豆31 分钟前
深入企业内部的MCP知识(三):FastMCP工具转换(Tool Transformation)全解析:从适配到增强的工具进化指南
java·前端·人工智能·大模型·github·mcp
豆苗学前端34 分钟前
手把手实现支持百万级数据量、高可用和可扩展性的穿梭框组件
前端·javascript·面试
不见_34 分钟前
不想再写周报了?来看看这个吧!
前端·命令行
yinke小琪36 分钟前
JavaScript 事件冒泡与事件捕获
前端·javascript
pany38 分钟前
写代码的节奏,正在被 AI 改写
前端·人工智能·aigc