黑马程序员技术交流社区

标题: async、await、generator核心逻辑 [打印本页]

作者: 绮罗    时间: 2020-5-22 09:27
标题: async、await、generator核心逻辑
async以下是群友回答。问题是 async 函数的实现原理,在单论 async 时,我觉得与 generator 并没有关系。


我们来看一下 MDN 上的描述:
async function 用来定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。
在看一下 MDN 上的转换结果:
For example, the following:
async function foo() {   return 1}复制代码is equivalent to:
function foo() {   return Promise.resolve(1)}复制代码
所以单论 async 的实现,它更应该类似于下面的代码:
function _async(fn) {    return (...args) => Promise.resolve(fn(...args));}复制代码generator再来看看 generator 是什么。先实现 generator 是为了在接下来的 await 中使用。以下是廖雪峰教程中的示例代码:
function* fib(max) {    var        t,        a = 0,        b = 1,        n = 0;    while (n < max) {        yield a;        [a, b] = [b, a + b];        n ++;    }    return;}var f = fib(5);f.next(); // {value: 0, done: false}f.next(); // {value: 1, done: false}f.next(); // {value: 1, done: false}f.next(); // {value: 2, done: false}f.next(); // {value: 3, done: false}f.next(); // {value: undefined, done: true}复制代码可以理解为调用 generator 返回的“对象”上的 next 方法,可以产生类似于 { value, done } 形式的结果。
再来看一下掘友写代码像蔡徐抻的9k字 | Promise/async/Generator实现原理解析这篇文章里写的 generator 转换结果:
// 代码function* foo() {  yield 'result1'  yield 'result2'  yield 'result3'}  const gen = foo()console.log(gen.next().value)console.log(gen.next().value)console.log(gen.next().value)// babel官网转换结果"use strict";var _marked =/*#__PURE__*/regeneratorRuntime.mark(foo);function foo() {  return regeneratorRuntime.wrap(function foo$(_context) {    while (1) {      switch (_context.prev = _context.next) {        case 0:          _context.next = 2;          return 'result1';        case 2:          _context.next = 4;          return 'result2';        case 4:          _context.next = 6;          return 'result3';        case 6:        case "end":          return _context.stop();      }    }  }, _marked);}var gen = foo();console.log(gen.next().value);console.log(gen.next().value);console.log(gen.next().value);复制代码在群里探讨的过程中,为了区别 async 和 await,写了如下的一段“generator”:
function get() {  let g = {    done: false,    count: 0,    next() {      if (this.count === 3) this.done = true;      if (this.done) return { value: this.count, done: this.done };      this.count++;      return { value: this.count, done: this.done };    }  }  return g;}let obj = get();console.log(obj.next())console.log(obj.next())console.log(obj.next())console.log(obj.next())console.log(obj.next())// 输出结果// { value: 1, done: false }// { value: 2, done: false }// { value: 3, done: false }// { value: 3, done: true }// { value: 3, done: true }复制代码以上代码与 babel 以及 ES 中的实现肯定差别很大,但是基本上就是一个 generator 的执行逻辑。具体可以参考 babel 转码后的结果中的 switch (_context.prev = _context.next) 以及几行 _context.next = 2; 代码。
await先回顾一下 await的作用,它能够将异步代码转变成同步代码的逻辑,在异步代码返回值到达前,程序将会被挂起。所以我们要挑一个能让程序挂起的代码,也就是 while(true)。实现如下:
function _await() {    let result = data.next();    while (true) {      console.log('waiting...', result);       if (result.done) return result.value;      result = data.next();    }}let g = get();console.log('before');let a = _await(g);console.log(a);console.log('after');// 输出// before// awaiting... { value: 1, done: false }// awaiting... { value: 2, done: false }// awaiting... { value: 3, done: false }// awaiting... { value: 3, done: true }// 3// after复制代码更进一步在实际使用过程中,await 只能在 async 中使用,群友问这个要怎么实现,于是折腾出下面这一版。
function myAwait() {  this.c = function(data) {    if (!this.isCalledByAsync) throw new Error('Should be called by async');    let result = data.next();    while (true) {      console.log('awaiting...', result);       if (result.done) return result.value;      result = data.next();    }  }}function myAsync(fn) {  myAwait.prototype.isCalledByAsync = true;  let m = get();  console.log('async before');  let d = new myAwait().c(m);  console.log(d);  console.log('async after');  myAwait.prototype.isCalledByAsync = false;}myAsync();let g = get();console.log('no async before');let a = new myAwait().c(g);console.log(a);console.log('no async after');// 输出// async before// awaiting... { value: 1, done: false }// awaiting... { value: 2, done: false }// awaiting... { value: 3, done: false }// awaiting... { value: 3, done: true }// 3// async after// no async before// Error: Should be called by async复制代码没想到其他什么办法来做这个标记,只能使用控制原型的方式来控制是否能够执行,但也算只能在 async 内部才能使用了。


作者:Wetoria
链接:https://juejin.im/post/5ec4f16bf265da76b74072e4
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2