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. Spring boot之JPA

    JPA 步骤: (1)在pom.xml添加mysql,spring-data-jpa依赖 (2)在application.properties文件中配置mysql连接配置文件 (3)在applicat ...

  2. opencv_将图像上的4个点按逆时针排序

    1:代码如下: #include "stdafx.h" #include "cxcore.h" #include "cvcam.h" #in ...

  3. SpringMvc的学习之路

    今天首先SpringMvc 写了个简单的配置 1.首先搭好环境配置web.xml <!-- 配置 DispatcherServlet --> <servlet> <ser ...

  4. 状压dfs小记

    一点前(tu)言(cao) 真的考起dfs来可谓是什么都能往dfs上套 状压不止能dp,还能与dfs结合成为搜索好(duliu)题 剪枝卡常司空见惯(打开题解一看并不是纯dfs,emmmm) 开始正文 ...

  5. java 传值

    好文章:https://zwmf.iteye.com/blog/1738574 public class Test { public int i,j; public void test_m(Test ...

  6. C++ TODO __fastcall

    C++ TODO __fastcall int __fastcall init_keys2(char *a1, char *a2) { char *v2; // r6 char *v3; // r5 ...

  7. 1.2.1 Maven到底是什么鬼

    解释之前,提1个小问题. 1.1.假如你正在Eclipse下开发两个Java项目,姑且把它们称为A.B,其中A项目中的一些功能依赖于B项目中的某些类,那么如何维系这种依赖关系的呢? 很简单,这不就是跟 ...

  8. Day1_Python基础一

    一.基本认识 1.计算机基础 CPU:计算 内存:缓存 硬盘:存储 操作系统:硬件与软件的桥梁 应用程序:应用的平台 2.Python的历史 1989年龟叔,追求清晰.简单.优美的原则. 主要领域:云 ...

  9. 使用matlab用优化后的梯度下降法求解达最小值时参数

    matlab可以用 -Conjugate gradient -BFGS -L-BFGS 等优化后的梯度方法来求解优化问题.当feature过多时,最小二乘计算复杂度过高(O(n**3)),此时 这一些 ...

  10. oracle 一张表插入另外一张表 存储过程

    ----创建存储过程 create or replace procedure inserttest as cursor cs is select id, name, cla, addr, phone, ...