0. 前言
在JavaScript中,代码几乎普遍依赖一个假定:一个函数一旦开始执行,就会运行到结束,期间不会有其他代码能够打断它并插入其间。然而,在 ES6 中,引入了一种新的函数类型,它可以使得函数在运行过程中被打断。这种类型被称为生成器。在这篇文章中,我们就来聊一聊这个新的神器——生成器(Generator),以及它是如何来解决异步问题的。在此之前,我们需要明确的一点是生成器其实并不是为解决一部问题而产生的,但是它又天生非常适合解决异步问题,也就是说生成器本身并不解决异步问题。
1. 生成器
我们首先来看生成器的使用方式:
var x = 1;
function *foo() {
x++;
yield;
console.log('x:', x);
}
function bar() {
x++;
}
这段代码中,函数foo()
就是一个生成器的类型,而字段 yield
的功能是打破函数内部运行。这就是我们在文章的开始提到的生成器可以打破函数内部的运行。现在,我们要让借助迭代器让这段代码运行起来。
var it = foo();
// 这里启动foo()
it.next();
x; // 2
bar(); // 3
it.next(); // x:3
这段代码的运行过程是这样的:
- 首先,
it = foo()
运算并没有执行生成器*foo()
,而是构造了一个迭代器,这个迭代器会控制它的运行 it.next()
这段代码启动了生成器,当生成器运行到yield
字段时,会暂停运行,保留现场。在我们这个例子当中,被保留的现场是变量x,在执行了x++
之后,它的值变成了2.- 此时,运行
bar()
函数,同样执行了x++
语句,此时的x值变成了3. - 最后,
it.next()
使得生成器函数从暂停出恢复运行,最后打印的结果是x:3
。
从这段代码的运行过程,我们也看得出来生成器函数在运行的过程中被打断了。在运行到 yield
时,中间执行了 bar()
函数才接着继续执行完成的。这个例子所展示的就是生成器。
在例子中,我们除了生成器,还提到了另一个概念——迭代器。我们使用它来控制生成器的运行。在这里,我们简单介绍一下迭代器的概念,关于迭代器详细的内容还请看这里的内容. MDN中的定义为:
在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。
2. 生成器 + Promise 解决异步
我们知道,生成器本身并不能解决异步问题,能够解决异步问题的就是我们之前提到的回调和Promise。而Promise又被称为异步问题最优雅的解决方案。不管怎么说,在这里我们解决异步问题的方案是Promise + 生成器。关于Promise,可以看这篇文章。
俗话说:“没有对比就没有伤害”。我们先来看纯粹的Promise版本的ajax请求:
function foo(url) {
return request(
url
);
}
foo(url)
.then(res => {
console.log('response:', res);
}, err => {
console.log('error:', err);
});
然后,我们再来看支持生成器版本的ajax请求:
function foo(url) {
return request(
url
);
}
function *main() {
try {
var text = yield foo(url);
console.log(res);
}
catch(err) {
console.log(err);
}
}
显然,使用生成器版本的代码要简介明了的多。不需要使用then()
方法就可以完成异步的实现,甚至不需要改动原来的同步代码就可以实现异步,丑陋的then
已经被屏蔽掉了。然后,我们使用迭代器来运行生成器就可以了。
var it = main();
var p = it.next().value;
p.then(
text => { it.next(text); },
err => { it.throw(err); }
);
3. async/await
如果你 await 了一个 Promise, async 函数就会自动获知要做什么,它会暂停这个函数(就像生成器一样),直到 Promise 决议。在这里,我们需要知道async...await
解决异步问题的方案其实就是生成器+Promise的语法糖。具体的使用方式如下:
async function() {
let a = await ajax(url)
//....
}
这种方法可以说是异步解决方案中最简洁的,也是书写起来最接近同步代码的解决方案了,在很多场合中,我们都会直接使用这种方案来完成异步代码的编写。但是要理解 async...await
内部究竟发生了什么,还是需要仔细理解Promise和生成器都是怎样工作的。