实现一个简单的async await

async await实际上是yield语法糖,当然我们首先得知道async await是怎么转换成yield的,

例如:

typescript 复制代码
async function(){
  await Promise.reject(1);
  return await Promise.reject(1)
}

实际上转换成的是(以下是简化过的):

javascript 复制代码
function a() {
    return __awaiter(function* () {
        yield Promise.reject(1);
        return yield Promise.reject(1);
    });
}
a();

__awaiter实际上就是用yield实现的async await语法糖。简单来说,就是用yield写一个自动执行器,并且返回一个Promise对象,Promise对象的状态由执行过程的情况而定。

所以我们可以很简单的得出以下代码:

typescript 复制代码
function __awaiter(fun: GeneratorFunction) {
  let resolveFn: (value: unknown) => void;
  let rejectFn: (value: unknown) => void;
  const generator = fun(); // 执行转换后的generator
  const promise = new Promise((resolve, reject) => {
    resolveFn = resolve;
    rejectFn = reject;
  });
  return promise; // 返回的promise
}

然后我们简单实现一个自动执行器:

typescript 复制代码
function __awaiter(fun: GeneratorFunction) {
  let resolveFn: (value: unknown) => void;
  let rejectFn: (value: unknown) => void;
  const generator = fun();
  const promise = new Promise((resolve, reject) => {
    resolveFn = resolve;
    rejectFn = reject;
  });
  
  // 如果yield之后返回的是简单类型,则把它包装成Promise
  const adopt = (value: unknown) => {
    return value instanceof Promise ? value : Promise.resolve(value);
  };

 // 自动执行器
  const step = (preValue?: unknown) => {
      const { value, done } = generator.next(preValue);
      adopt(value).then(
        nextValue => {
           // empty
        },
        reason => {
           // empty
        },
      );
  };

  // 执行自动执行器
  step();
  return promise;
}

但是我们可以看到adopt后面的then函数还是空的,这里实际上就是要根据执行状态来改变Promise对象的状态。

执行过程中的情况大概分为三种:

1. 执行过程顺利,prmiosevalue取决于最后一次generator.next()的值。

所以很简单,我们可以得出如下代码:

typescript 复制代码
function __awaiter(fun: GeneratorFunction) {
  let resolveFn: (value: unknown) => void;
  let rejectFn: (value: unknown) => void;
  const generator = fun();
  const promise = new Promise((resolve, reject) => {
    resolveFn = resolve;
    rejectFn = reject;
  });
  
  // 如果yield之后返回的是简单类型,则把它包装成Promise
  const adopt = (value: unknown) => {
    return value instanceof Promise ? value : Promise.resolve(value);
  };

 // 自动执行器
  const step = (preValue?: unknown) => {
      const { value, done } = generator.next(preValue);
      adopt(value).then(
        nextValue => {
           // 执行一切顺利
           // 执行一切顺利
           // 执行一切顺利
           // 执行一切顺利
           // 执行一切顺利
           if (done) {
            resolveFn(nextValue);
          } else {
            step(nextValue);
          }
        },
        reason => {
           // empty
        },
      );
  };

  // 执行自动执行器
  step();
  return promise;
}

2 . 执行过程中出现问题,例如直接throw xxx,抛出错误,此时需要在next()外面包裹try{}catch{},并且将错误原因赋值给rejectFn函数,并中断自动执行器。

从一个yield另一个yield中间发生了错误,这时需要在执行器外层嵌套try{}catch{}

typescript 复制代码
function __awaiter(fun: GeneratorFunction) {
  let resolveFn: (value: unknown) => void;
  let rejectFn: (value: unknown) => void;
  const generator = fun();
  const promise = new Promise((resolve, reject) => {
    resolveFn = resolve;
    rejectFn = reject;
  });

  const adopt = (value: unknown) => {
    return value instanceof Promise ? value : Promise.resolve(value);
  };

  const step = (preValue?: unknown) => {
  // 外层嵌套try catch
  // 外层嵌套try catch
  // 外层嵌套try catch
  // 外层嵌套try catch
  // 外层嵌套try catch
  // 外层嵌套try cat
    try {
      const { value, done } = generator.next(preValue);

      adopt(value).then(
        nextValue => {
          if (done) {
            resolveFn(nextValue);
          } else {
            step(nextValue);
          }
        },
        reason => {
          // empty
        },
      );
    } catch (e) {
      rejectFn(e);
    }
  };

  step();
  return promise;
}

3. yield(await)后面的promise对象变为reject状态,并中断自动执行器

typescript 复制代码
function __awaiter(fun: GeneratorFunction) {
  let resolveFn: (value: unknown) => void;
  let rejectFn: (value: unknown) => void;
  const generator = fun();
  const promise = new Promise((resolve, reject) => {
    resolveFn = resolve;
    rejectFn = reject;
  });

  const adopt = (value: unknown) => {
    return value instanceof Promise ? value : Promise.resolve(value);
  };

  const step = (preValue?: unknown) => {
    try {
      const { value, done } = generator.next(preValue);

      adopt(value).then(
        nextValue => {
          if (done) {
            resolveFn(nextValue);
          } else {
            step(nextValue);
          }
        },
        reason => {
          // await 后面的promise变为了reject
          // await 后面的promise变为了reject
          // await 后面的promise变为了reject        
          if (done) {
            rejectFn(reason);
          } else {
            step(reason);
          }
        },
      );
    } catch (e) {
      rejectFn(e);
    }
  };

  step();
  return promise;
}

通过以上条件,得到最终代码:

typescript 复制代码
function __awaiter(fun: GeneratorFunction) {
  let resolveFn: (value: unknown) => void;
  let rejectFn: (value: unknown) => void;
  const generator = fun();
  const promise = new Promise((resolve, reject) => {
    resolveFn = resolve;
    rejectFn = reject;
  });

  const adopt = (value: unknown) => {
    return value instanceof Promise ? value : Promise.resolve(value);
  };

  const step = (preValue?: unknown) => {
    try {
      const { value, done } = generator.next(preValue);

      adopt(value).then(
        nextValue => {
          if (done) {
            resolveFn(nextValue);
          } else {
            step(nextValue);
          }
        },
        reason => {
          if (done) {
            rejectFn(reason);
          } else {
            step(reason);
          }
        },
      );
    } catch (e) {
      rejectFn(e);
    }
  };

  step();
  return promise;
}
相关推荐
岭南灯火12 分钟前
前端通用交互式几何编辑器的开发经验
前端·设计模式·架构
岭南灯火21 分钟前
前端通用交互式几何编辑器的设计法则 7 - 设计模式与原则
前端·设计模式·架构
岭南灯火24 分钟前
前端通用交互式几何编辑器的设计法则 4 - 绘制、悬停与编辑
前端·设计模式·架构
岭南灯火29 分钟前
前端通用交互式几何编辑器的设计法则 3 - 数据对象的设计
前端·javascript·架构
Jackson__29 分钟前
从 LLM 到 Agent:一篇文章搞懂 AI 圈热词!
前端·agent·ai编程
岭南灯火37 分钟前
前端通用交互式几何编辑器的设计法则 2 - 交互事件流
前端·javascript·架构
岭南灯火38 分钟前
前端通用交互式几何编辑器的设计法则 6 - 配套设施与工程化
前端·设计模式·架构
岭南灯火39 分钟前
前端通用交互式几何编辑器的设计法则 5 - 辅助元素的设计
前端·设计模式·架构
岭南灯火44 分钟前
前端通用交互式几何编辑器的设计法则 1 - 入口的对象及其成员
前端·javascript·架构
满栀5851 小时前
Vue.js 接口封装最佳实践:从基础到高级
前端·javascript·vue.js