前言

本文就是简单介绍下 Async 语法编译后的代码。

Async

  1. const fetchData = (data) => new Promise((resolve) => setTimeout(resolve, 1000, data + 1))
  2.  
  3. const fetchValue = async function () {
  4. var value1 = await fetchData(1);
  5. var value2 = await fetchData(value1);
  6. var value3 = await fetchData(value2);
  7. console.log(value3)
  8. };
  9.  
  10. fetchValue();
  11. // 大约 3s 后输出 4

Babel

我们直接在 Babel 官网的 Try it out 粘贴上述代码,然后查看代码编译成什么样子:

  1. "use strict";
  2.  
  3. function _asyncToGenerator(fn) {
  4. return function() {
  5. var gen = fn.apply(this, arguments);
  6. return new Promise(function(resolve, reject) {
  7. function step(key, arg) {
  8. try {
  9. var info = gen[key](arg);
  10. var value = info.value;
  11. } catch (error) {
  12. reject(error);
  13. return;
  14. }
  15. if (info.done) {
  16. resolve(value);
  17. } else {
  18. return Promise.resolve(value).then(
  19. function(value) {
  20. step("next", value);
  21. },
  22. function(err) {
  23. step("throw", err);
  24. }
  25. );
  26. }
  27. }
  28. return step("next");
  29. });
  30. };
  31. }
  32.  
  33. var fetchData = function fetchData(data) {
  34. return new Promise(function(resolve) {
  35. return setTimeout(resolve, 1000, data + 1);
  36. });
  37. };
  38.  
  39. var fetchValue = (function() {
  40. var _ref = _asyncToGenerator(
  41. /*#__PURE__*/ regeneratorRuntime.mark(function _callee() {
  42. var value1, value2, value3;
  43. return regeneratorRuntime.wrap(
  44. function _callee$(_context) {
  45. while (1) {
  46. switch ((_context.prev = _context.next)) {
  47. case 0:
  48. _context.next = 2;
  49. return fetchData(1);
  50.  
  51. case 2:
  52. value1 = _context.sent;
  53. _context.next = 5;
  54. return fetchData(value1);
  55.  
  56. case 5:
  57. value2 = _context.sent;
  58. _context.next = 8;
  59. return fetchData(value2);
  60.  
  61. case 8:
  62. value3 = _context.sent;
  63.  
  64. console.log(value3);
  65.  
  66. case 10:
  67. case "end":
  68. return _context.stop();
  69. }
  70. }
  71. },
  72. _callee,
  73. this
  74. );
  75. })
  76. );
  77.  
  78. return function fetchValue() {
  79. return _ref.apply(this, arguments);
  80. };
  81. })();
  82.  
  83. fetchValue();

_asyncToGenerator

regeneratorRuntime 相关的代码我们在 《ES6 系列之 Babel 将 Generator 编译成了什么样子》 中已经介绍过了,这次我们重点来看看 _asyncToGenerator 函数:

  1. function _asyncToGenerator(fn) {
  2. return function() {
  3. var gen = fn.apply(this, arguments);
  4. return new Promise(function(resolve, reject) {
  5. function step(key, arg) {
  6. try {
  7. var info = gen[key](arg);
  8. var value = info.value;
  9. } catch (error) {
  10. reject(error);
  11. return;
  12. }
  13. if (info.done) {
  14. resolve(value);
  15. } else {
  16. return Promise.resolve(value).then(
  17. function(value) {
  18. step("next", value);
  19. },
  20. function(err) {
  21. step("throw", err);
  22. }
  23. );
  24. }
  25. }
  26. return step("next");
  27. });
  28. };
  29. }

以上这段代码主要是用来实现 generator 的自动执行以及返回 Promise。

当我们执行 fetchValue() 的时候,执行的其实就是 _asyncToGenerator 返回的这个匿名函数,在匿名函数中,我们执行了

  1. var gen = fn.apply(this, arguments);

这一步就相当于执行 Generator 函数,举个例子:

  1. function* helloWorldGenerator() {
  2. yield 'hello';
  3. yield 'world';
  4. return 'ending';
  5. }
  6.  
  7. var hw = helloWorldGenerator();

var gen = fn.apply(this, arguments) 就相当于 var hw = helloWorldGenerator();,返回的 gen 是一个具有 next()、throw()、return() 方法的对象。

然后我们返回了一个 Promise 对象,在 Promise 中,我们执行了 step("next"),step 函数中会执行:

  1. try {
  2. var info = gen[key](arg);
  3. var value = info.value;
  4. } catch (error) {
  5. reject(error);
  6. return;
  7. }

step("next") 就相当于 var info = gen.next(),返回的 info 对象是一个具有 value 和 done 属性的对象:

  1. {value: Promise, done: false}

接下来又会执行:

  1. if (info.done) {
  2. resolve(value);
  3. } else {
  4. return Promise.resolve(value).then(
  5. function(value) {
  6. step("next", value);
  7. },
  8. function(err) {
  9. step("throw", err);
  10. }
  11. );
  12. }

value 此时是一个 Promise,Promise.resolve(value) 依然会返回这个 Promise,我们给这个 Promise 添加了一个 then 函数,用于在 Promise 有结果时执行,有结果时又会执行 step("next", value),从而使得 Generator 继续执行,直到 info.done 为 true,才会 resolve(value)

不完整但可用的代码

  1. (function() {
  2. var ContinueSentinel = {};
  3.  
  4. var mark = function(genFun) {
  5. var generator = Object.create({
  6. next: function(arg) {
  7. return this._invoke("next", arg);
  8. }
  9. });
  10. genFun.prototype = generator;
  11. return genFun;
  12. };
  13.  
  14. function wrap(innerFn, outerFn, self) {
  15. var generator = Object.create(outerFn.prototype);
  16.  
  17. var context = {
  18. done: false,
  19. method: "next",
  20. next: 0,
  21. prev: 0,
  22. sent: undefined,
  23. abrupt: function(type, arg) {
  24. var record = {};
  25. record.type = type;
  26. record.arg = arg;
  27.  
  28. return this.complete(record);
  29. },
  30. complete: function(record, afterLoc) {
  31. if (record.type === "return") {
  32. this.rval = this.arg = record.arg;
  33. this.method = "return";
  34. this.next = "end";
  35. }
  36.  
  37. return ContinueSentinel;
  38. },
  39. stop: function() {
  40. this.done = true;
  41. return this.rval;
  42. }
  43. };
  44.  
  45. generator._invoke = makeInvokeMethod(innerFn, context);
  46.  
  47. return generator;
  48. }
  49.  
  50. function makeInvokeMethod(innerFn, context) {
  51. var state = "start";
  52.  
  53. return function invoke(method, arg) {
  54. if (state === "completed") {
  55. return { value: undefined, done: true };
  56. }
  57.  
  58. context.method = method;
  59. context.arg = arg;
  60.  
  61. while (true) {
  62. state = "executing";
  63.  
  64. if (context.method === "next") {
  65. context.sent = context._sent = context.arg;
  66. }
  67.  
  68. var record = {
  69. type: "normal",
  70. arg: innerFn.call(self, context)
  71. };
  72.  
  73. if (record.type === "normal") {
  74. state = context.done ? "completed" : "yield";
  75.  
  76. if (record.arg === ContinueSentinel) {
  77. continue;
  78. }
  79.  
  80. return {
  81. value: record.arg,
  82. done: context.done
  83. };
  84. }
  85. }
  86. };
  87. }
  88.  
  89. window.regeneratorRuntime = {};
  90.  
  91. regeneratorRuntime.wrap = wrap;
  92. regeneratorRuntime.mark = mark;
  93. })();
  94.  
  95. "use strict";
  96.  
  97. function _asyncToGenerator(fn) {
  98. return function() {
  99. var gen = fn.apply(this, arguments);
  100. return new Promise(function(resolve, reject) {
  101. function step(key, arg) {
  102. try {
  103. var info = gen[key](arg);
  104. var value = info.value;
  105. } catch (error) {
  106. reject(error);
  107. return;
  108. }
  109. if (info.done) {
  110. resolve(value);
  111. } else {
  112. return Promise.resolve(value).then(
  113. function(value) {
  114. step("next", value);
  115. },
  116. function(err) {
  117. step("throw", err);
  118. }
  119. );
  120. }
  121. }
  122. return step("next");
  123. });
  124. };
  125. }
  126.  
  127. var fetchData = function fetchData(data) {
  128. return new Promise(function(resolve) {
  129. return setTimeout(resolve, 1000, data + 1);
  130. });
  131. };
  132.  
  133. var fetchValue = (function() {
  134. var _ref = _asyncToGenerator(
  135. /*#__PURE__*/
  136. regeneratorRuntime.mark(function _callee() {
  137. var value1, value2, value3;
  138. return regeneratorRuntime.wrap(
  139. function _callee$(_context) {
  140. while (1) {
  141. switch ((_context.prev = _context.next)) {
  142. case 0:
  143. _context.next = 2;
  144. return fetchData(1);
  145.  
  146. case 2:
  147. value1 = _context.sent;
  148. _context.next = 5;
  149. return fetchData(value1);
  150.  
  151. case 5:
  152. value2 = _context.sent;
  153. _context.next = 8;
  154. return fetchData(value2);
  155.  
  156. case 8:
  157. value3 = _context.sent;
  158.  
  159. console.log(value3);
  160.  
  161. case 10:
  162. case "end":
  163. return _context.stop();
  164. }
  165. }
  166. },
  167. _callee,
  168. this
  169. );
  170. })
  171. );
  172.  
  173. return function fetchValue() {
  174. return _ref.apply(this, arguments);
  175. };
  176. })();
  177.  
  178. fetchValue();

请原谅我水了一篇文章……

ES6 系列

ES6 系列目录地址:https://github.com/mqyqingfeng/Blog

ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。