用generator实现async/await的效果

91 阅读3分钟

在这之前从来没有接触过generator的用法,它的写法和async/await很相似,所以看了看相关用法,以及是否能像async/await那样实现异步的链式调用。

generator跟普通函数的区别:在于generator函数是可以暂停执行的,需要暂停的地方都用yield来控制

generator的用法

//定义
function* gen() {
  //后面跟的常量
    yield 1
    yield 2

  //后面跟的函数
  //   yield fn2(3);
  //   yield fn2(4);

  //后面跟的是promise
  // yield fn3(5);
  // yield fn3(6);
}
function fn2(num) {
  return num;
}
function fn3(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num + 1);
    }, 1000);
  });
}
//调用
const g = gen();
console.log(g.next()); //{ value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: undefined, done: true } 到最后一步的时候done为true,这里因为gen函数没有返回值,所以value等于undefined

//若yield 后面为函数,执行结果依次为
{ value: 3, done: false }
{ value: 4, done: false }
{ value: undefined, done: true }

//若yield后面跟的是异步,则value收到的是一个promise,取值需要.then
//异步获取返回值
const g1 = g.next();
console.log("----", g1); // { value: Promise { <pending> }, done: false }
g1.value.then((res) => {
  console.log(res); //6
});
const g2 = g.next();
console.log("----", g2);// { value: Promise { <pending> }, done: false }
g2.value.then((res) => {
  console.log(res);//7
});

模仿async/await

//async/await的写法
async function asyncFn(){
    const res=await getList()
    const res2=await getNumList(res)
}

//分析
//1.async 返回的是一个promise
//2. res2会在res执行过后才会执行赋值
//3. await 返回的是promise中的resolve()或者reject()结果
//generator实现

function* gen3() {
  const num1 = yield fn3(111);
  const num2 = yield fn3(num1);
  return 33;
}

const g3 = gen3();
let g31 = g3.next();
console.log("1", g31); // 1 { value: Promise { <pending> }, done: false }
!g31.done &&
  g31.value.then((res1) => {
    console.log("res1", res1); //res1 112 一秒后打出
    let g32 = g3.next(res1);
    console.log("2", g32);
    !g32.done &&
      g32.value.then((res2) => {
        console.log("res2", res2); //res2 113 //2秒后
        let g33 = g3.next(res2);
        console.log("3", g33); // 3 { value: 33, done: true }
        // !g33.done && ...
      });
  });
  
//结果:每隔一秒,就会打印出一个res
1 { value: Promise { <pending> }, done: false }
res1 112
2 { value: Promise { <pending> }, done: false }
res2 113
3 { value: 33, done: true }
//以上操作很像await的操作,但是gen并不会返回一个promise,而且链式调用需要手动去控制(万一它有几十个以上的调用呢?)

实现代码

//分析
//1.generator需要重新封装一下,使得它的返回值是一个promise (高阶函数)
//2. yield需要有链式调用的效果(自动判断)
function generatorChange(fn) {
  return function () {
    return new Promise((resolve, reject) => {
      const g = fn();
      //   const next1 = g.next();
      // next1.value.then()...
      //最后一层的时候resolve(结果)

      const go = (val, doname) => {
        let result;
        try {
          result = g[doname](val);
        } catch (error) {
          return reject(error); // 报错的话会走catch,直接reject
        }
        let { value, done } = result;
        console.log("next执行", result);
        if (done) {
          //结束了
          return resolve(value);
        } else {
          //注意:value可能是一个常量,也可能是一个promise,且promise有可能是成功或者失败

          return Promise.resolve(value).then(
            (res) => {
              console.log("next参数传递",res);
              go(res, "next");
            },
            (err) => {
              go(err, "throw");
            }
          );
        }
      };

      go("", "next"); //第一次的传参不会起作用
    });
  };
}



//调用
const asyncFn = generatorChange(gen3);

asyncFn().then((res) => console.log(res));

注意事项

关于next的传参 :

1.next 是先执行右边的yield xx ,然后下一次next才会接收第一次的参数;

2.next只有第二次传参才会有用,第一次没用

function* gen() {
  const num1 = yield fn2(11);
  console.log("--此时才接参数赋值--", num1);
  const num2 = yield fn2(22);
  console.log("--此时才接参数赋值--", num2);
  return 33;
}

const g = gen();
console.log(g.next(111)); 
//{ value: 11, done: false }
console.log(g.next(222));
//--此时才接参数赋值-- 222 (这里直接接收的是next(222)的值,第一次next的传参被忽略掉了)
//{ value: 22, done: false }
console.log(g.next(333));
//--此时才接参数赋值-- 333
//{ value: 33, done: true }