Generator函数
1.理解基本概念
- 可以把它理解成一个状态机,封装了多个内部状态。
- 执行
Generator函数会返回一个遍历器对象,返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。 - 有两个特征:一是,
function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态。 - 调用与普通函数一样,但调用后并不会执行,会返回一个指向内部状态的指针对象(即遍历器对象)。
- 必须调用遍历器对象的
next方法,使得指针移向下一个状态。
2.yield表达式
- 是暂停标志。
-
yield表达式只能用在Generator函数里面,用在其他地方都会报错。 -
yield表达式本身没有返回值,或者说总是返回undefined。
遍历器对象的next方法的运行逻辑如下。
(1)遇到
yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。
需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行。
yield表达式与return语句对比:
相似: 都能返回紧跟在语句后面的那个表达式的值。
区别: 每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。
一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield表达式。正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield。
3.与 Iterator 接口的关系
任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数(即Generator函数)。
4.next()方法参数
- 可以带一个参数,该参数就会被当作上一个
yield表达式的返回值。 - 在第一次使用
next方法时,传递参数是无效的。 - 如果想要第一次调用
next方法时,就能够输入值,可以在Generator函数外面再包一层。function wrapper(generatorFunction) { return function (...args) { let generatorObject = generatorFunction(...args); generatorObject.next(); return generatorObject; }; } const wrapped = wrapper(function* () { console.log(`First input: ${yield}`); return 'DONE'; }); wrapped().next('hello!')
5.for...of循环
- 可以自动遍历
Generator函数时生成的Iterator对象,且此时不再需要调用next方法。一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象。
6.Generator.prototype.throw()
Generator 函数返回的遍历器对象,都有一个throw方法:
- 可以在函数体外抛出错误,然后在
Generator函数体内捕获。 - 可以接受一个参数,该参数会被
catch语句接收,建议抛出Error对象的实例。 - 被捕获以后,会附带执行下一条
yield表达式。也就是说,会附带执行一次next方法。 - 只要
Generator函数内部部署了try...catch代码块,那么遍历器的throw方法抛出的错误,不影响下一次遍历。
Generator函数体内抛出的错误,也可以被函数体外的catch捕获。
7.Generator.prototype.return()
Generator函数返回的遍历器对象,还有一个return方法:
- 可以返回给定的值,并且终结遍历
Generator函数。 - 不提供参数,则返回值的
value属性为undefined。 - 如果
Generator函数内部有try...finally代码块,那么return方法会推迟到finally代码块执行完再执行。(即调用return方法后,就开始执行finally代码块,然后等到finally代码块执行完,再执行return方法。)
8.next()、throw()、return() 的共同点
都是让 Generator函数恢复执行,并且使用不同的语句替换yield表达式。
-
next()是将yield表达式替换成一个值。如果next方法没有参数,就相当于替换成undefined。 -
throw()是将yield表达式替换成一个throw语句。 -
return()是将yield表达式替换成一个return语句。
9.yield* 表达式
- 用来在一个
Generator函数里面执行另一个Generator函数。(表明返回的是一个遍历器对象) -
yield*后面的Generator 函数(没有return语句时),是for...of的一种简写形式。有return语句时,则需要用var value = yield* iterator的形式获取return语句的值。 - 任何数据结构只要有
Iterator接口,就可以被yield*遍历。
考文档:Generator 函数的语法
async函数
实现原理:
async其实就是Generator函数的语法糖。将generator函数和它的自动执行器co包装了下。
特点:
- 内置执行器
- 将异步代码以同步的写法展示,和更好的语义化(async/await),便于理解;
- 有更广的适用性,
async函数内部的await后可以是Promise对象,也可以是原始类型(等同于同步操作)。
用法
- 像普通函数一样去调用
async函数。 - 同
Generator函数一样,async函数会立即返回一个Promise对象,可以使用then方法添加回调函数。 - 当函数执行的时候,一旦遇到
await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。(继发执行)
使用过程中的需要注意的点:
1.await后的Promise对象可能是rejected,所以需要有相应的错误处理机制。
async function func() {
// 放在try{ } catch(){ }中
try {
await getDataPromise();
} catch(err) {
console.log(err);
}
// 或者
await getDataPromise().catch(err => {
console.log(err);
});
}
2.同样,await不能出现在普通函数中。