generator 到 async 的简单理解。
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 {
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 的简单理解。的更多相关文章
- 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制
[原创]分布式之数据库和缓存双写一致性方案解析(三) 正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...
- JavaScript异步编程:Generator与Async
从Promise开始,JavaScript就在引入新功能,来帮助更简单的方法来处理异步编程,帮助我们远离回调地狱. Promise是下边要讲的Generator/yield与async/await的基 ...
- JS异步编程 (2) - Promise、Generator、async/await
JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...
- 简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析
简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析 虽然经常用 OAuth 2.0,但是原理却不曾了解,印象里觉得很简单,请求跳来跳去,今天看完相关介绍,就来捋一捋 ...
- input屏蔽历史记录 ;function($,undefined) 前面的分号是什么用处 JSON 和 JSONP 两兄弟 document.body.scrollTop与document.documentElement.scrollTop兼容 URL中的# 网站性能优化 前端必知的ajax 简单理解同步与异步 那些年,我们被耍过的bug——has
input屏蔽历史记录 设置input的扩展属性autocomplete 为off即可 ;function($,undefined) 前面的分号是什么用处 ;(function($){$.ex ...
- git的简单理解及基础操作命令
前端小白一枚,最近开始使用git,于是花了2天看了廖雪峰的git教程(偏实践,对于学习git的基础操作很有帮助哦),也在看<git版本控制管理>这本书(偏理论,内容完善,很不错),针对所学 ...
- 简单理解Struts2中拦截器与过滤器的区别及执行顺序
简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...
- [转]简单理解Socket
简单理解Socket 转自 http://www.cnblogs.com/dolphinX/p/3460545.html 题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公 ...
- Js 职责链模式 简单理解
js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...
随机推荐
- webpack学习之路--demo1
1.不使用框架建立webpack项目时 (1).npm init -y 生成package.json文件 (2).npm install --save-dev webpack 在当前项目下安装webp ...
- spark 笔记 4:Apache Hadoop YARN: Yet Another Resource Negotiator
spark支持YARN做资源调度器,所以YARN的原理还是应该知道的:http://www.socc2013.org/home/program/a5-vavilapalli.pdf 但总体来说, ...
- ajaxGird修改一条记录中的字段
var rowData = ajaxgrid.getSelectedRow(); var quality = rowData["quality"]; var rowIndex = ...
- eclipse subclipse svn 插件安装使用
一.在线安装 1.打开Eclipse,菜单栏中选择"Help"->"Install New SoftWare..." 2.在弹出的对话框中,点击" ...
- 方法二:Excel 2016 VBA工程密码破解
将你要破解的Excel文件关闭,切记一定要关闭呀!然后新建一个Excel文件 打开新建的这个Excel,按下alt+F11,打开vb界面,新建一个模块,如图所示 将代码复制到这个模块中,代码如下:Pr ...
- Django模型的Field Types
Field Types 常用参数: null 如果设置为 True , Django 存放一个 NULL 到数据库字段.默认为 False. allow_null 如果设置为 True , 该字段将接 ...
- python的dict,set,list,tuple应用详解
python的dict,set,list,tuple应用详解 本文深入剖析了python中dict,set,list,tuple应用及对应示例,有助于读者对其概念及原理的掌握.具体如下: 1.字典(d ...
- Android的消息机制之ThreadLocal的工作原理
ThreadLocal 可以把一个对象保存在指定的线程中,对象保存后,只能在指定线程中获取保存的数据,对于其他线程来说则无法获取到数据. 日常开发中 ThreadLocal 使用的地方比较少,但是系统 ...
- ELK+Kafka
kafka:接收java程序投递的消息的日志队列 logstash:日志解析,格式化数据为json并输出到es中 elasticsearch:实时搜索搜索引擎,存储数据 kibana:基于es的数据可 ...
- 20191209 Linux就该这么学(5)
5. 用户身份与文件权限 5.1 用户的身份和能力 Linux 系统的管理员之所以是 root,并不是因为它的名字叫 root,而是因为该用户的身份号码即 UID( User IDentificati ...