什么是Generator函数。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态。Generator 函数最大特点就是可以交出函数的执行权(即暂停执行)。
为了更好的理解上面这句话,我决定给大家举个例子
1 function* Generator() {
2 yield '11111111';
3 yield '22222222'
4 return '3333333';
5 }
6
7 let aaa = Generator();
1 let a = aaa.next()
2 let b = aaa.next()
3 let c = aaa.next()
4 let d = aaa.next()
5 console.log(a,b,c,d)
输出结果: {value: "11111111", done: false} {value: "22222222", done: false} {value: "3333333", done: true} {value: undefined, done: true}
想要Generator函数执行下一步,必须调用遍历器对象的next
方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式或return语句。由此可见,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。也就是上面说的可以交出函数的执行权。
async函数
文章最一开始我们就说了async函数就是Generator函数的语法糖。
为什么这么说呢,我还是决定给大家举个例子吧:
(盗用ES6中对比Generator函数和async函数的例子)
代码上看起来,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await。
实际上async函数对Generator函数的改进,体现在一下四点:
1.async函数自带执行器,所以执行方式和普通函数的执行方式一样,通过函数名+()的方式执行。
2.async和await比起*和yield在语义上更清楚。
3.co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
4.async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
用法
async函数 声明函数中存在异步操作,执行结果返回一个 Promise 对象,可以使用then方法添加回调函数。await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
当函数执行的时候,遇到await会先等待await后面的函数执行,等待异步操作完成之后再继续执行。
1 async function consoleA(){
2 let result = await run(2,3)
3 console.log("B",result)
4 return "consoleA 方法"
5 }
6
7 function run(x,y){
8 console.log('run 方法')
9 return x*y
10 }
11 consoleA()
12 .then(res => {
13 console.log(res,'then')
14 })
输出顺序为 :
run 方法
B 6
consoleA 方法 then
注意:
1.async函数在声明形式上和普通函数没有区别,函数声明式,函数表达式,对象方法,class方法和箭头函数等都可以声明async函数。
2.任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
3.async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
我是一个简单又无辜的小问题~~~
1 function consoleA(){
2 console.log("A")
3 }
4
5 async function consoleB(){
6 await consoleA()
7 console.log("B")
8 }
9
10 (function consoleC(){
11 consoleB().then(_ => {
12 console.log("D")
13 })
14 console.log("C")
15 })()
错误处理
上面我们说了,await后面的异步操作异常,那么整个async函数就会全部停止执行。但是有的时候我们又想,可不可以即使前面的异步操作异常,后面的代码还是可以继续执行。
解决办法就是把异步操作放在try catch语句中
例如
1 async function consoleA(){
2 try{
3 console.log("A")
4 await run1()
5 await run2()
6 await run3()
7 }catch(e){}
8
9 console.log("运行")
10 }
11 function run1(){
12 throw new Error("错误")
13 }
14 function run2(){
15 throw new Error("错误")
16 }
17 function run2(){
18 throw new Error("错误")
19 }
20 consoleA()
上面代码中异步操作抛出的异常被catch捕获,所以console.log(“运行”)依然可以正常输出。
实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
1 async function fn(args) {
2 // ...
3 }
4
5 // 等同于
6
7 function fn(args) {
8 return spawn(function* () {
9 // ...
10 });
11 }
所以所有的async函数都可以写成上面例子中下面的那种形式,其中的spawn函数就是自动执行器
问题总结:
1.async和await,await会返回异步方法resolve的结果,所以如果用了async和await又需要捕捉异常就需要用到try catch这中可以捕捉错误的机制
2. async 在函数声明前使用async关键词,说明函数中存在异步操作。await 等待代码执行完毕再继续向下执行
3.try catch 异常捕获机制。在try语句中的代码抛出异常,都会在catch中被捕获 。try代码块中只要有一个抛出了异常,就不会在继续向下执行