shfshanyue / Daily-Question

互联网大厂内推及大厂面经整理,并且每天一道面试题推送。每天五分钟,半年大厂中
https://q.shanyue.tech
4.95k stars 510 forks source link

【Q240】如何实现一个 async/await #241

Open shfshanyue opened 4 years ago

sl1673495 commented 4 years ago
/**
 * 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)
})
crossz commented 4 years ago

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

shfshanyue commented 3 years ago

参考 @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);
    });
  };
}
qiutian00 commented 3 years ago

赞赞赞。

iceycc commented 3 years ago

(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);
hviwen commented 8 months ago
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
}
JLUssh commented 3 months ago

参考 @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.