基本实现
在Promise之前,我们经历着痛苦的回调地狱,但是Promise的then链式调用也存在着大量的代码嵌套,可读性不好,如要实现a -> b -> c的异步流程,用Promise需要如下写法:
Promise.resolve(a).then(() => {
// b code
}).then(() => {
// c code
})
在Promise之后出现了Generator的方式,通过yeild的方式,把函数的执行挂起,再通过generator.next()切换到下一个状态,提供了一种优雅的异步编程解决方案,实现如上写法:
function* myGenerator() {
yield Promise.resolve(1);
yield Promise.resolve(2);
// 注意此处 yield 和 return 的区别,return 只需执行3次next 最后value会有值,yield 4次无值
// yield Promise.resolve(3);
return Promise.resolve(3);
}
let gen = myGenerator();
// 此时GeneratorState 为suspended
gen.next();
gen.next();
gen.next(); // { value: 3, done: true }
// 此时GeneratorState 为closed
Generator不久之后出现终级大杀器了async/await,实际上async/await就是Generator的语法糖,实现如上的功能:
async () => {
const a = await Promise.resolve(1);
const b = await Promise.resolve(2);
const c = await Promise.resolve(3);
}
但两种还是有两明显的区别:
1、async/await会自执行代码块,而生成器需要手动调用next 2、async函数返回的是Promise,而Generator返回的是生成器对象
如何让执行器自动执行
function run(gen) {
return new Promise((resolve, reject) => {
let g = gen();
function _next(val) {
let res;
try {
res = g.next(val);
} catch(e) {
reject(e);
}
if (res.done) {
return resolve(res.value)
}
Promise.resolve(res.value).then((val) => {
_next(val);
})
}
})
}
async/await实现
// 编译前代码
export default async function openPage() {
const newWindow = window.open('', '_blank');
try {
const url = await Promise.resolve('www.baidu.com');
newWindow.location.href = url;
} catch (e) {
newWindow.close();
}
}
// 编译后代码
// 类似上文 run _next方法
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg); // 此时的info 为 invoke执行返回的{value, done}
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
// 类似上文 run 方法
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args); // fn目前绑定过GP(generator prototype)的callee
// gen就是regeneratorRuntime.wrap生成的generator对象
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
}
_next(undefined);
});
};
}
const regeneratorRuntime = {}
// mark主要就是给生成器函数(foo)绑定了一系列原型
regeneratorRuntime.mark = function(genFun) {
genFun.__proto__ = GeneratorFunctionPrototype;
genFun.prototype = Object.create(Gp);
return genFun;
}
// wrap方法先是创建了一个generator,并继承outerFn.prototype;然后new了一个context对象;makeInvokeMethod方法接收innerFn(对应foo$)、context和this,并把返回值挂到generator._invoke上;最后return了generator。
regeneratorRuntime.wrap = (innerFn, outerFn, self) => {
var generator = Object.create(outerFn.prototype);
var context = new Context([]);
// 低配版context
// var context = {
// next:0,
// prev: 0,
// done: false,
// stop: function stop () {
// this.done = true
// }
}
generator._invoke = makeInvokeMethod(innerFn, self, context);
return generator;
}
// 创建invoke(真正的next)方法
function makeInvokeMethod(innerFn, context) {
// 将状态置为start
var state = "start";
return function invoke(method, arg) {
// 已完成
if (state === "completed") {
return { value: undefined, done: true };
}
context.method = method;
context.arg = arg;
// 执行中
while (true) {
state = "executing";
var record = {
type: "normal",
arg: innerFn.call(self, context) // 执行下一步,并获取状态(其实就是switch里边return的值)
};
if (record.type === "normal") {
// 判断是否已经执行完成
state = context.done ? "completed" : "yield";
// ContinueSentinel其实是一个空对象,record.arg === {}则跳过return进入下一个循环
// 什么时候record.arg会为空对象呢, 答案是没有后续yield语句或已经return的时候,也就是switch返回了空值的情况(跟着上面的switch走一下就知道了)
if (record.arg === ContinueSentinel) {
continue;
}
// next()的返回值
return {
value: record.arg,
done: context.done
};
}
}
};
}
// async/await内函数本体被转化后的代码
function _openPage() {
_openPage = _asyncToGenerator(
/*#__PURE__*/ regeneratorRuntime.mark(function _callee() {
var newWindow, url;
return regeneratorRuntime.wrap(
function _callee¥(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
newWindow = window.open('', '_blank');
_context.prev = 1;
_context.next = 4;
return Promise.resolve('www.baidu.com');
case 4:
url = _context.sent;
newWindow.location.href = url;
_context.next = 11;
break;
case 8:
_context.prev = 8;
_context.t0 = _context['catch'](1);
newWindow.close();
case 11:
case 'end':
return _context.stop();
}
}
},
_callee,
null,
[[1, 8]],
);
}),
);
return _openPage.apply(this, arguments);
}
❝
我们分析一下调用流程:我们定义的async函数被转化为以上代码 转化后的代码分为三大块:_callee¥(_context)由yield分割生成器函数代码而来 全局变量context对象用于储存函数执行上下文 invoke()方法定义next(),用于执行_callee¥(_context)来跳到下一步 当我们调用g.next(),就相当于调用invoke()方法,执行gen$(_context),进入switch语句,switch根据context的标识,执行对应的case块,return对应结果 当生成器函数运行到末尾(没有下一个yield或已经return),switch匹配不到对应代码块,就会return空值,这时g.next()返回{value: undefined, done: true}
❞