高级前端js【Q240】如何实现一个 async/await

如何实现一个 async/await

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 241

Author 回答者: sl1673495

/**
 * async的执行原理
 * 其实就是自动执行generator函数
 * 暂时不考虑genertor的编译步骤(更复杂)
 */
 
const getData = () =>
  new Promise((resolve) => setTimeout(() => resolve("data"), 1000));
 
// 这样的一个async函数 应该再1秒后打印data
async function test() {
  const data = await getData();
  console.log("data: ", data);
  const data2 = await getData();
  console.log("data2: ", data2);
  return "success";
}
 
// async函数会被编译成generator函数 (babel会编译成更本质的形态,这里我们直接用generator)
function* testG() {
  // await被编译成了yield
  const data = yield getData();
  console.log("data: ", data);
  const data2 = yield getData();
  console.log("data2: ", data2);
  return "success";
}
 
function asyncToGenerator(generatorFunc) {
  return function () {
    const gen = generatorFunc.apply(this, arguments);
 
    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let generatorResult;
        try {
          generatorResult = gen[key](arg);
        } catch (error) {
          return reject(error);
        }
 
        const { value, done } = generatorResult;
 
        if (done) {
          return resolve(value);
        } else {
          return Promise.resolve(value).then(
            function onResolve(val) {
              step("next", val);
            },
            function onReject(err) {
              step("throw", err);
            },
          );
        }
      }
      step("next");
    });
  };
}
 
const testGAsync = asyncToGenerator(testG);
testGAsync().then((result) => {
  console.log(result);
});

Author 回答者: crossz

公众号里这篇文章留的 github 网址有误,那个 url 打开 404 :)

Author 回答者: shfshanyue

参考 @bebel/runtime 的实现代码如下,可在 asyncToGenerator.js 查看源代码

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
 
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}
 
export default function _asyncToGenerator(fn) {
  return function () {
    var self = this,
      args = arguments;
    return new Promise(function (resolve, reject) {
      var gen = fn.apply(self, args);
 
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
      }
 
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
      }
 
      _next(undefined);
    });
  };
}

Author 回答者: qiutian00

赞赞赞。

Author 回答者: iceycc

(function (done) {
  if (!done) return;
  const getData = () => {
    return new Promise((resolve) => setTimeout(() => resolve("data"), 1000));
  };
  function* testG() {
    // await被编译成了yield
    const data = yield getData();
    console.log("data: ", data);
    const data2 = yield getData();
    console.log("data2: ", data2);
    return "success";
  }
  function genratorWarp(testG) {
    return new Promise((resolve, reject) => {
      let it = testG();
      function next(val) {
        let { value, done } = it.next(val);
        if (done) {
          resolve(value);
        } else {
          Promise.resolve(value).then((data) => {
            next(data);
          }, reject);
        }
      }
      next();
    });
  }
  genratorWarp(testG).then((data) => {
    console.log(data);
  });
})(1);

Author 回答者: hviwen

function _awaiter(fn) {
  let resolveFn = (value) => value;
  let rejectFn = (reason) => reason;
 
  let generator = fn;
 
  let promise = new Promise((resolve, reject) => {
    resolveFn = resolve;
    rejectFn = reject;
  });
 
  const adopt = (value) => {
    return value instanceof Promise ? value : Promise.resolve(value);
  };
 
  const step = (preValue) => {
    try {
      const { value, done } = generator.next(preValue);
      adopt(value).then(
        (nextValue) => {
          if (done) {
            resolveFn(nextValue);
          } else {
            step();
          }
        },
        (reason) => {
          if (done) {
            rejectFn(reason);
          } else {
            step();
          }
        },
      );
    } catch (err) {
      rejectFn(err);
    }
  };
 
  step();
  return promise;
}

Author 回答者: JLUssh

参考 @bebel/runtime 的实现代码如下,可在 asyncToGenerator.js 查看源代码

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
 
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}
 
export default function _asyncToGenerator(fn) {
  return function () {
    var self = this,
      args = arguments;
    return new Promise(function (resolve, reject) {
      var gen = fn.apply(self, args);
 
      function _next(value) {
        asyncGeneratorStep(
          gen,
          resolve,
          reject,
          _next,
          _throw,
          "next",
          value,
        );
      }
 
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
      }
 
      _next(undefined);
    });
  };
}

请问_throw是否有必要给? 还有var gen = fn.apply(self, args); 是否有必要确定this指向,这里的self应该指向全局global or window.