前言
群里聊天有人说面试问到了 async
的实现原理,并且回答中提到了 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
内部才能使用了。
结语
本文是个人学习过程中的理解,如果有理解有误的地方,欢迎各位大佬指教。 本文写出来也是为了各位学习代码的过程中增加一些不同的角度。