Generator函数与async总结
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
不能出现在普通函数中。