generator 到 async 的简单理解。

2023-05-25,,

generator 到 async 的简单理解。觉得实现方式很有意思。

1. generator

generator 函数返回一个遍历器对象

遍历器对象 每次调用next 方法 返回 有着value 和done 两个属性的对象

generator 函数 yield 后面的表达式即为 返回对象 value属性的值

举个简单例子:

generator 函数返回一个遍历器

遍历器对象每执行一次next() 都只执行了generator 函数内部部分代码,遇到yield本次执行就结束了。

借助工具查看generator 经过转换后的代码,来了解一下generator 的大概实现

源码

 function *gen() {
console.log('开始')
let a = yield '第一步'
console.log(a)
let b = yield '第二步'
console.log(b)
let c = yield '第三步'
console.log(c)
} var it = gen()
console.log(it.next(''))
console.log(it.next())
console.log(it.next())
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属性的值

 // 第一步通过将原函数简单转换 (babel 编译过程中的节点修改可以了解一下)
// function genSourceCode() {
// console.log('开始')
// let a = yield '第一步'
// console.log(a)
// let b = yield '第二步'
// console.log(b)
// let c = yield '第三步'
// console.log(c)
// }
// 原函数变成由gen数中的每个 yield 表达式分割的片段都重写为 switch case的新函数
function gen$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
console.log('开始');
_context.next = 3;
return '第一步'; case 3:
a = _context.sent;
console.log(a);
_context.next = 7;
return '第二步'; case 7:
b = _context.sent;
console.log(b);
_context.next = 11;
return '第三步'; case 11:
c = _context.sent;
console.log(c); case 13:
case "end":
return _context.stop();
}
}
}
// context
var context = {
next:0,
prev: 0,
sent: undefined, // 这个值是用来记住每次调用next函数传递的参数
done: false,
stop: function stop () {
this.done = true
}
} let gen = function() {
return {
next: function() {
value = context.done ? undefined: gen$(context)
done = context.done
return {
value,
done
}
}
}
}
var it = gen()
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

3. generator产生的迭代器对象 ,迭代自执行

手动麻烦,产生了自执行的需求:

 function* gen() {
console.log('开始')
let a = yield '第一步'
console.log(a)
let b = yield '第二步'
console.log(b)
let c = yield '第三步'
console.log(c)
} var it = gen()
console.log(it.next(''))
console.log(it.next())
console.log(it.next())
console.log(it.next()) function co(gen) {
let it = gen()
return new Promise((resolve, reject) => {
!(function next(lastValue) {
let { value, done } = it.next(lastValue)
console.log({ value, done })
if (done) {
resolve(value)
} else {
Promise.resolve(value).then(next,reason => reject(reason))
}
})()
})
}
co(gen)

自执行函数,会将上一次it.next()得到的value值传递到下一个it.next()输出中 下面这行代码值得思考

Promise.resolve(value).then(next,reason => reject(reason))

4. async的简单实现

async函数,实现 基于 generator 函数和自动执行器。

 function spawn(gen) {
return new Promise((resolve, reject) => {
const it = gen()
!function step(nextFn) {
try{
var {value, done } = nextFn()
} catch(e) {
reject(e)
return
}
if (done) {
resolve(value)
} else {
Promise.resolve(value).then((value)=>{
step((value)=>it.next(value))
},()=>{
step((value)=>it.throw(value))
})
}
}(()=>it.next(undefined))
})
}
function* gen() {
try {
var a = yield new Promise((resolve,reject) =>{
setTimeout(()=>{
reject(100)
},1000)
})
} catch(e) {
}
let b = yield new Promise((resolve,reject) =>{
setTimeout(()=>{
resolve(102)
},1000)
})
return a + b
} async function asyncDemo() {
try {
var a = await new Promise((resolve,reject) =>{
setTimeout(()=>{
reject(100)
},1000)
})
} catch(e) { }
let b = await new Promise((resolve,reject) =>{
setTimeout(()=>{
resolve(102)
},1000)
})
return a + b
} spawn(gen).then((value)=>{
console.log('spawn-->onfulfilled:',value)
},(value)=>{
console.log('spawn-->onRejected:',value)
})
asyncDemo().then((value)=>{
console.log('asyncDemo-->onfulfilled:',value)
},(value)=>{
console.log('asyncDemo-->onRejected:',value)
})

运行上面代码 对async理解就比较深刻了。async 的内部实现generator 函数和自执行函数 。

5.总结

需要认真理解的:

函数转换成 switch case 组成的函数(代码有点似状态机模型)

async 的内部实现包括了generator 函数和自执行函数

思考 : 为何 try catch 包裹了  await rejected 的promise 后续代码才能继续执行

 try {
var a = await new Promise((resolve, reject) => {
setTimeout(() => {
reject(100)
}, 1000)
})
} catch (e) {
console.log(e)
}

看了下面代码,

 async function asyncDemo() {
console.log('asyncDemo')
let a = await new Promise((resolve,reject) =>{
setTimeout(()=>{
reject(100)
},1000)
})
console.log('asyncDemo--->b')
//为何下面代码没有执行
let b = await new Promise((resolve,reject) =>{
setTimeout(()=>{
resolve(102)
},1000)
})
return a + b
} async function asyncDemo2() {
console.log('asyncDemo2')
try {
var a = await new Promise((resolve,reject) =>{
setTimeout(()=>{
reject(100)
},1000)
})
} catch(e){
console.log(e)
}
//为何下面代码执行了 自执行出错有try catch 时候会增加一步走catch节点。
console.log('asyncDemo2--->b')
let b = await new Promise((resolve,reject) =>{
setTimeout(()=>{
resolve(102)
},1000)
})
return a + b
}
asyncDemo().then(null,(reason)=>{
console.log('asyncDemo:',reason)
})
asyncDemo2().then((reason)=>{
console.log('asyncDemo:',reason)
})

工具查看转化的代码,自执行出错有try catch 时候会增加一步走catch节点。

当case的promise rejected 的时候context.next 会被改变成case 6,

如图case 6:执行后没break 和return  则继续执行 case 8

generator 到 async 的简单理解。的相关教程结束。

《generator 到 async 的简单理解。.doc》

下载本文的Word格式文档,以方便收藏与打印。