实现一个简单的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. 执行过程顺利,prmiose的value取决于最后一次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;
}
相关推荐
程序员海军4 小时前
用了两年多 GPT,我最近开始换模型了,还折腾了个求职避坑 Agent
前端·人工智能·后端
怕浪猫5 小时前
红利窗口开着,程序员别在场边囤课
面试·github·ai编程
zttbee5 小时前
echarts图表tooltip色块失效
前端·javascript·echarts
excel6 小时前
IndexNow:让搜索引擎更快发现网站更新的主动通知协议
前端·后端·架构
子兮曰7 小时前
CSS 复现 23 种设计风格:从拟物到液态玻璃的成本阶梯与兜底写法【实战】
前端·css·视觉设计
虚无境7 小时前
如何用AI帮我写出实用的插件
前端·javascript·ai编程
柳杉8 小时前
从数字化到数智化:3D 可视化大屏的下一个五年
前端·ai编程·数据可视化
lll23526917798 小时前
HR如何快速整理面试沟通内容?
面试·职场和发展
zimoyin8 小时前
在 C# 里嵌入 JavaScript:用 Jint 做可热更新的自动化脚本(从入门到踩坑)
javascript·c#·自动化
零售校长9 小时前
西湖区零售门店店长面试怎么准备
面试·职场和发展·零售