generator 到 async 的简单理解。觉得实现方式很有意思。
1. generator
generator 函数返回一个遍历器对象
遍历器对象 每次调用next 方法 返回 有着value 和done 两个属性的对象
generator 函数 yield 后面的表达式即为 返回对象 value属性的值
举个简单例子:
generator 函数返回一个遍历器
遍历器对象每执行一次next() 都只执行了generator 函数内部部分代码,遇到yield本次执行就结束了。
借助工具查看generator 经过转换后的代码,来了解一下generator 的大概实现
源码
1 function *gen() {
2 console.log('开始')
3 let a = yield '第一步'
4 console.log(a)
5 let b = yield '第二步'
6 console.log(b)
7 let c = yield '第三步'
8 console.log(c)
9 }
10
11 var it = gen()
12 console.log(it.next(''))
13 console.log(it.next())
14 console.log(it.next())
15 console.log(it.next())
转换后的代码如图(有图可见,原来的gen函数代码被转换成switch case的函数了,这个函数,就像状态机,状态不同,跳转执行的结果不同)
如图,查看源码,左边函数,被准换成右边带有状态的switch 片段
执行it.next() 的时候,内部就会调用左边的while 包裹的函数,默认_context_next = 0 (_context是内部用来存储 状态 ,next传入参数,等值得)
将_context_next 的值付给_context_prev,_context_prev就是当前传入switch的值,执行对应的case片段,修改 _context_next的值 ,return 跳出while循环
下一次调用it.next() 的时候,内部又调用左边的while 包裹的函数,将_context_next 的值付给_context_prev,_
context_prev就是当前传入switch的值,执行对应的case片段,修改 _context_next的值,return 跳出while循环
如此重复上面片段 直到 执行第四个 it.next的时候 ,执行对应的case片段,没有return 接着会执行 case: end
执行 _context_stop() 函数,得到 第四个 it.next()的返回值,{value:undefined, done: true} 迭代结束。
借助查看babel,regenerator的实现 查找上图的几个函数,来看看以下细节
regeneratorRuntime.mark (包裹函数,返回新函数,新函数能生成迭代器)
_context (保留函数执行的上下文状态)
新的$gen,由gen数中的每个 yield 表达式分割的片段都重写为 switch case的函数,每个 case 中使用 _context 来保存函数当前的上下文状态。
- regeneratorRuntime.wrap (设置调用函数,这个地方设计的特别好,暴露接口,由makeInvokeMehtod来设置具体的invoke方法)
看看 makeInvokeMethod 返回的 invoke 方法
从上面分析可以看出 不断调用next方法 就是不断调用 switch case($gen函数) , _context做记录
再次调用next方法 方法 因为标记状态变了,执行的case 就变了。
2. generator 简单实现
generator 函数返回一个遍历器对象,对象有next方法。
遍历器对象每次调用next 方法 返回 有着value 和done 两个属性的对象
generator 函数 yield 后面的表达式即为 返回对象 value属性的值
1 // 第一步通过将原函数简单转换 (babel 编译过程中的节点修改可以了解一下)
2 // function genSourceCode() {
3 // console.log('开始')
4 // let a = yield '第一步'
5 // console.log(a)
6 // let b = yield '第二步'
7 // console.log(b)
8 // let c = yield '第三步'
9 // console.log(c)
10 // }
11 // 原函数变成由gen数中的每个 yield 表达式分割的片段都重写为 switch case的新函数
12 function gen$(_context) {
13 while (1) {
14 switch (_context.prev = _context.next) {
15 case 0:
16 console.log('开始');
17 _context.next = 3;
18 return '第一步';
19
20 case 3:
21 a = _context.sent;
22 console.log(a);
23 _context.next = 7;
24 return '第二步';
25
26 case 7:
27 b = _context.sent;
28 console.log(b);
29 _context.next = 11;
30 return '第三步';
31
32 case 11:
33 c = _context.sent;
34 console.log(c);
35
36 case 13:
37 case "end":
38 return _context.stop();
39 }
40 }
41 }
42 // context
43 var context = {
44 next:0,
45 prev: 0,
46 sent: undefined, // 这个值是用来记住每次调用next函数传递的参数
47 done: false,
48 stop: function stop () {
49 this.done = true
50 }
51 }
52
53 let gen = function() {
54 return {
55 next: function() {
56 value = context.done ? undefined: gen$(context)
57 done = context.done
58 return {
59 value,
60 done
61 }
62 }
63 }
64 }
65 var it = gen()
66 console.log(it.next())
67 console.log(it.next())
68 console.log(it.next())
69 console.log(it.next())
3. generator产生的迭代器对象 ,迭代自执行
手动麻烦,产生了自执行的需求:
1 function* gen() {
2 console.log('开始')
3 let a = yield '第一步'
4 console.log(a)
5 let b = yield '第二步'
6 console.log(b)
7 let c = yield '第三步'
8 console.log(c)
9 }
10
11 var it = gen()
12 console.log(it.next(''))
13 console.log(it.next())
14 console.log(it.next())
15 console.log(it.next())
16
17 function co(gen) {
18 let it = gen()
19 return new Promise((resolve, reject) => {
20 !(function next(lastValue) {
21 let { value, done } = it.next(lastValue)
22 console.log({ value, done })
23 if (done) {
24 resolve(value)
25 } else {
26 Promise.resolve(value).then(next,reason => reject(reason))
27 }
28 })()
29 })
30 }
31 co(gen)
自执行函数,会将上一次it.next()得到的value值传递到下一个it.next()输出中 下面这行代码值得思考
Promise.resolve(value).then(next,reason => reject(reason))
4. async的简单实现
async函数,实现 基于 generator 函数和自动执行器。
1 function spawn(gen) {
2 return new Promise((resolve, reject) => {
3 const it = gen()
4 !function step(nextFn) {
5 try{
6 var {value, done } = nextFn()
7 } catch(e) {
8 reject(e)
9 return
10 }
11 if (done) {
12 resolve(value)
13 } else {
14 Promise.resolve(value).then((value)=>{
15 step((value)=>it.next(value))
16 },()=>{
17 step((value)=>it.throw(value))
18 })
19 }
20 }(()=>it.next(undefined))
21 })
22 }
23 function* gen() {
24 try {
25 var a = yield new Promise((resolve,reject) =>{
26 setTimeout(()=>{
27 reject(100)
28 },1000)
29 })
30 } catch(e) {
31 }
32 let b = yield new Promise((resolve,reject) =>{
33 setTimeout(()=>{
34 resolve(102)
35 },1000)
36 })
37 return a + b
38 }
39
40 async function asyncDemo() {
41 try {
42 var a = await new Promise((resolve,reject) =>{
43 setTimeout(()=>{
44 reject(100)
45 },1000)
46 })
47 } catch(e) {
48
49 }
50 let b = await new Promise((resolve,reject) =>{
51 setTimeout(()=>{
52 resolve(102)
53 },1000)
54 })
55 return a + b
56 }
57
58 spawn(gen).then((value)=>{
59 console.log('spawn-->onfulfilled:',value)
60 },(value)=>{
61 console.log('spawn-->onRejected:',value)
62 })
63 asyncDemo().then((value)=>{
64 console.log('asyncDemo-->onfulfilled:',value)
65 },(value)=>{
66 console.log('asyncDemo-->onRejected:',value)
67 })
运行上面代码 对async理解就比较深刻了。async 的内部实现generator 函数和自执行函数 。
5.总结
函数转换成 switch case 组成的函数(代码有点似状态机模型)
async 的内部实现包括了generator 函数和自执行函数
1 try {
2 var a = await new Promise((resolve, reject) => {
3 setTimeout(() => {
4 reject(100)
5 }, 1000)
6 })
7 } catch (e) {
8 console.log(e)
9 }
1 async function asyncDemo() {
2 console.log('asyncDemo')
3 let a = await new Promise((resolve,reject) =>{
4 setTimeout(()=>{
5 reject(100)
6 },1000)
7 })
8 console.log('asyncDemo--->b')
9 //为何下面代码没有执行
10 let b = await new Promise((resolve,reject) =>{
11 setTimeout(()=>{
12 resolve(102)
13 },1000)
14 })
15 return a + b
16 }
17
18 async function asyncDemo2() {
19 console.log('asyncDemo2')
20 try {
21 var a = await new Promise((resolve,reject) =>{
22 setTimeout(()=>{
23 reject(100)
24 },1000)
25 })
26 } catch(e){
27 console.log(e)
28 }
29 //为何下面代码执行了 自执行出错有try catch 时候会增加一步走catch节点。
30 console.log('asyncDemo2--->b')
31 let b = await new Promise((resolve,reject) =>{
32 setTimeout(()=>{
33 resolve(102)
34 },1000)
35 })
36 return a + b
37 }
38 asyncDemo().then(null,(reason)=>{
39 console.log('asyncDemo:',reason)
40 })
41 asyncDemo2().then((reason)=>{
42 console.log('asyncDemo:',reason)
43 })
工具查看转化的代码,自执行出错有try catch 时候会增加一步走catch节点。
当case的promise rejected 的时候context.next 会被改变成case 6,
如图case 6:执行后没break 和return 则继续执行 case 8