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 ... 
随机推荐
- ES6指北【1】——let、const
			1.如何学习ES6 1.1 js的学习顺序 ES5 -> ES6 -> ES7 -> ES8 以此类推 ES5没学好就别想学好ES6 1.2 边学边用 学了就要用 2.变量声明的方式 ... 
- LeetCode 148. 排序链表(Sort List)
			题目描述 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序. 示例 1: 输入: 4->2->1->3 输出: 1->2->3->4 示例 ... 
- MSO Transponder 页面开发思路
			1. 确定Transponder开发页面分类,定义负责模块 2. 定义模块页面布局 3. 选择页面各数据对应的控件类型 4. 选定控件对应set/get所用方式 快捷键链接设置: http://www ... 
- Is JavaScript a pass-by-reference or pass-by-value language?
			Is JavaScript a pass-by-reference or pass-by-value language? A very detailed explanation about copyi ... 
- leetcode-easy-trees-101. Symmetric Tree-YES
			mycode 92.44% # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, ... 
- UVA 572 -- Oil Deposits(DFS求连通块+种子填充算法)
			UVA 572 -- Oil Deposits(DFS求连通块) 图也有DFS和BFS遍历,由于DFS更好写,所以一般用DFS寻找连通块. 下述代码用一个二重循环来找到当前格子的相邻8个格子,也可用常 ... 
- 评【TED】陆克文:中美注定要冲突吗?
			陆克文TED演讲地址:https://www.bilibili.com/video/av2196100?from=search&seid=6953438337852168205 实话说,这篇材 ... 
- leetcode 714. 买卖股票的最佳时机含手续费
			继承leetcode123以及leetcode309的思路,,但应该也可以写成leetcode 152. 乘积最大子序列的形式 class Solution { public: int maxProf ... 
- ceph集群添加osd
			获取osd的ID 这个操作是在管理节点上执行 [root@node-4 osd]# ceph osd create 2 对磁盘做处理 [root@node-4 ~]# parted /dev/sdb ... 
- 为解决Thymeleaf数字格式化问题而想到的几种方案
			背景: spring后端输出double类型数据,前端使用thymeleaf框架,格式化double数据类型,由于需要显示原值(比如原来录入5,而不能显示5.00),因此需要存储数值(存储值为deci ... 
