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 ...
随机推荐
- 作业要求20191010-9 alpha week 1/2 Scrum立会报告+燃尽图 07
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/8752 一.小组情况组长:贺敬文组员:彭思雨 王志文 位军营 杨萍队名:胜 ...
- spark MLlib 概念 4: 协同过滤(CF)
1. 定义 协同过滤(Collaborative Filtering)有狭义和广义两种意义: 广义协同过滤:对来源不同的数据,根据他们的共同点做过滤处理. Collaborative filterin ...
- shell sed应用
sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行替换.删除.新增.选取等特定工作,下面先了解一下sed的用法sed命令行格式为:sed [-nefri] ...
- jquery数组的合并 对象的合并
直接转自:https://www.cnblogs.com/ooo0/p/7737678.html 1.数组的合并 1.1 concat方法 var a=[1,2,3],b=[4,5,6]; var c ...
- ggsci: error while loading shared libraries: libnnz11.so: cannot open shared object file
完整的错误信息如下: ggsci: error while loading shared libraries: libnnz11.so: cannot open shared object file: ...
- Java代码审计-铁人下载系统
初学 java 代码审计,跟着表哥们脚步,走一遍审计流程,就选了个没有使用 Java 框架的 java 系统,作为入门. 目的是为了熟悉代码审计流程,寻找漏洞的思路,入门记录. 准备工作 为了验证审计 ...
- git使用遇到的问题
1.我新建了一个django项目,然后又在git上新建了一个仓库,然后我在django的项目文件内,将git上的项目clone到这个文件内的时候 git clone https://gitee.com ...
- 基于java config的springSecurity--session并发控制
原作地址:http://blog.csdn.net/xiejx618/article/details/42892951 参考资料:spring-security-reference.pdf的Sessi ...
- Nova 启动虚拟机流程解析
目录 文章目录 目录 前言 从请求说起 nova-api service 阶段 前言 Nova 启动虚拟机的东西太多,持续更新- 从请求说起 无论是通过 Dashboard 还是 CLI 启动一个虚拟 ...
- docker run 和 docker exec 的差异
docker run 和 docker exec 的差异 docker run :根据镜像创建一个容器并运行一个命令,操作的对象是 镜像: docker exec :在运行的容器中执行命令,操作的对象 ...