文章内容输出来源:拉勾教育 大前端高薪训练营
前言
在JavaScript异步编程【上】和 JavaScript异步编程【中】中,我们已经讲到了处理异步编程的两种方法:回调函数 和 Promise。
那么,我们再来介绍两种ES6中新增的处理异步编程的方法:Generator 和 Async/await 。
Generator
Generator 函数是 ES2015 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
基本介绍
生成器(Generator)函数能生成一组值的序列,但每个值的生成是基于每次请求,并不同于标准函数那样立即生成,每当生成器函数生成了一个值,它都会暂停执行但不会阻塞后续代码执行。
特征与语法
-
特征
1,function关键字与函数名之间有一个星号;
2,函数体内部使用yield表达式,生成一个新的值。 -
语法
代码示例如下:
function * fn () { console.log('1111'); yield 100 console.log('2222'); yield 200 console.log('3333'); yield 300 } const generator = fn() // 未执行,得到一个生成器对象
function* 这种声明方式(function关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个 Generator 对象。
代码分析
在上面的代码中,当调用fn函数的时候,会自动返回 Generator 对象,此时使用 generator 进行存储这个对象。然而,生成器函数在执行过程中,一旦遇到yield关键字,函数的执行将会被暂停下来,只有使用 Generator 对象的next()方法,才会让这个函数的函数体开始执行,并且,yield后面的值将会作为 next 的结果返回。
总的来说就是,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
代码示例如下:
function* fn () { yield 100 yield 200 return 300 } const generator = fn() // 未执行,得到一个生成器对象 generator.next() // { value: 100, done: false} generator.next() // { value: 200, done: false} generator.next() // { value: 300, done: true} generator.next() // { value: undefined, done: true}
上面代码一共调用了四次next方法。
代码分析
第一次调用,Generator 函数开始执行,直到遇到第一个yield表达式为止。next方法返回一个对象,它的value属性就是当前yield表达式的值100,done属性的值false,表示遍历还没有结束。
第二次调用,Generator 函数从上次yield表达式停下的地方,一直执行到下一个yield表达式。next方法返回的对象的value属性就是当前yield表达式的值200,done属性的值false,表示遍历还没有结束。
第三次调用,Generator 函数从上次yield表达式停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。
第四次调用,此时 Generator 函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。
-
总结
Generator 函数执行过程中,遇到yield关键字,函数的执行将会被暂停下来,只有调用Generator 对象的next()方法,才会让这个函数继续执行,并且yield后面的值将会以对象的形式进行返回,返回的对象中包含两个属性:一是value,当前的返回的值;二是done,它是一个布尔值,表示是否遍历结束。
yield 与 return
通过上面的描述,可以知道 yield 与 return 一样都可以返回值,那么,他们有何相同点和不同点呢?
-
相同点
都能返回紧跟在语句后面的那个表达式的值。
-
不同点
1,每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。
2,一个函数里,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield表达式。
3,正常函数只能返回一个值,因为只能执行一次return;Generator 函数可以返回一系列的值,因为可以有任意多个yield。
Async/await
async/await 是ES2017提供的处理异步函数的方式,它其实是 Generator 函数的语法糖。
-
语法
代码示例如下:
const fn = async function () { await 100 await 200 return 300 }
可以看到,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await。
async函数对 Generator 函数的改进,体现在以下四点。
-
1,内置执行器。
Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
fn();
上面的代码调用了fn函数,然后它就会自动执行,输出最后结果。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。
-
2,更好的语义。
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
-
3,更广的适用性。
co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
-
4,返回值是 Promise。
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
总结
async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。