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 的简单理解。的更多相关文章

  1. 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制

    [原创]分布式之数据库和缓存双写一致性方案解析(三)   正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...

  2. JavaScript异步编程:Generator与Async

    从Promise开始,JavaScript就在引入新功能,来帮助更简单的方法来处理异步编程,帮助我们远离回调地狱. Promise是下边要讲的Generator/yield与async/await的基 ...

  3. JS异步编程 (2) - Promise、Generator、async/await

    JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...

  4. 简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析

    简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析 虽然经常用 OAuth 2.0,但是原理却不曾了解,印象里觉得很简单,请求跳来跳去,今天看完相关介绍,就来捋一捋 ...

  5. input屏蔽历史记录 ;function($,undefined) 前面的分号是什么用处 JSON 和 JSONP 两兄弟 document.body.scrollTop与document.documentElement.scrollTop兼容 URL中的# 网站性能优化 前端必知的ajax 简单理解同步与异步 那些年,我们被耍过的bug——has

    input屏蔽历史记录   设置input的扩展属性autocomplete 为off即可 ;function($,undefined) 前面的分号是什么用处   ;(function($){$.ex ...

  6. git的简单理解及基础操作命令

    前端小白一枚,最近开始使用git,于是花了2天看了廖雪峰的git教程(偏实践,对于学习git的基础操作很有帮助哦),也在看<git版本控制管理>这本书(偏理论,内容完善,很不错),针对所学 ...

  7. 简单理解Struts2中拦截器与过滤器的区别及执行顺序

    简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...

  8. [转]简单理解Socket

    简单理解Socket 转自 http://www.cnblogs.com/dolphinX/p/3460545.html  题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公 ...

  9. Js 职责链模式 简单理解

    js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...

随机推荐

  1. java 从txt文本中随机获取名字

    代码: /* 获取随机文件文字 */ public static String random(String path) {//路径 String name = null; try { //把文本文件中 ...

  2. 文件和Stream

    I/O和文件 输入/输出(I/O)就是在内存和外部设备之间复制数据的过程.输入(input)就是从I/O设备复制数据到内存,输出(output)就是从内存复制数据到I/O设备. 一个文件可以理解成一串 ...

  3. ubuntu16.04修改host上外網

    1.打开hosts文件: sudo emacs /etc/hosts 2.加入下面的内容 #chrome同步服务器 203.208.46.132 chrome.google.com203.208.46 ...

  4. go GTK msys2

    1 下载安装msys2 MSYS2 (Minimal SYStem 2) 是一个MSYS的独立改写版本,主要用于 shell 命令行开发环境.同时它也是一个在Cygwin (POSIX 兼容性层) 和 ...

  5. oracle delete 数据恢复

    /*1.FLASHBACK QUERY*/ --闪回到15分钟前 select *  from orders  as of timestamp (systimestamp - interval ''1 ...

  6. 线程同步synchronized理解

    Synchronized 理解 用法:1.同步方法.2.同步静态方法.3同步代码块. 理解Synchronized 的关键是“锁” (原理在最后) 同步代码有“锁”者执行.所谓的锁必须是同一个.静态的 ...

  7. 学习笔记 - Git

    学习参考网址:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 Git是目前世界上 ...

  8. 软件结构B/S和C/S

    C/S(Client  Server)结构的软件: 比如: QQ. 极品飞车. 飞信 . 迅雷 缺点:更新的时候需要用户下载更新包然后再安装,程序员则需要开发客户端与服务端. 优点: 减轻服务端的压力 ...

  9. Eureka入门一(了解概念)

    Eureka注册中心(8761端口) IDEA(开发工具) 1,创建项目勾选Eureka Server 2, 创建yml文件,拷贝配置,下面配置必须为false,意为,该项目不要作为客户端注册,因为本 ...

  10. Jmeter (四)聚合报告详解