一步一步理解Generator函数的原理
作者:麦乐Generator函数基本用法
来源:恒生LIGHT云社区
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
Generator函数调用后生成的就是一个迭代器对象,可以通过调用迭代器的next方法,控制函数内部代码的执行。
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
Generator函数遇到yield,可以在生成器函数内部暂停代码的执行使其挂起。在可迭代对象上调用next()方法可以使代码从暂停的位置开始继续往下执行。
先来了解一下什么是可迭代对象?
可迭代对象 要成为可迭代 对象, 一个对象必须实现
@@iterator
方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator
的属性,可通过常量 Symbol.iterator
访问该属性:[
Symbol.iterator
] 一个无参数的函数,其返回值为一个符合迭代器协议的对象。let someString = "hi";
typeof someString[Symbol.iterator];
let iterator = someString[Symbol.iterator]();
iterator + "";
// "[object String Iterator]"
iterator.next();
// { value: "h", done: false }
iterator.next();
// { value: "i", done: false }
iterator.next();
// { value: undefined, done: true }
再来看一下,挂起是怎么回?有一个新的名词“协程”。
什么是协程? 协程(Coroutines)是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程。
协程不是被操作系统内核所管理的,而是完全由程序所控制,也就是在用户态执行。这样带来的好处是性能大幅度的提升,因为不会像线程切换那样消耗资源。
协程不是进程也不是线程,而是一个特殊的函数,这个函数可以在某个地方挂起,并且可以重新在挂起处外继续运行。所以说,协程与进程、线程相比并不是一个维度的概念。
Generator 函数原理 Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。
总结下来就是:
- 一个线程存在多个协程
- Generator函数是协程在ES6的实现
- Yield挂起协程(交给其它协程),next唤起协程
自动执行的Generator函数 可以根据g.next()的返回值{value: '', done: false}中done的值让Generator函数递归自执行:
function run(generator) {
var g = generator();
var next = function() {
var obj = g.next()
console.log(obj)
if(!obj.done) {
next()
}
}
next()
}
run(helloWorldGenerator)
这样写能实现自执行功能,但是 不能保证执行顺序。模拟两个异步请求:
function sleep1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('sleep1')
resolve(1)
}, 1000)
})
}
function sleep2() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('sleep2')
resolve(2)
}, 1000)
})
}
修改函数
function* helloWorldGenerator() {
yield sleep1();
console.log(1);
yield sleep2();
console.log(2);
}
执行
run(helloWorldGenerator)
看一下打印顺序:文章图片
异步函数还是在同步代码执行完以后执行的,如果想要实现异步代码也能按照顺序执行,可以对代码进一步优化:
function run(generator) {
var g = generator();
var next = function() {
var obj = g.next();
console.log(obj)
if(obj.done) return;
// 如果yield后面返回值不是promise,可以使用Promise.resolve包裹一下,防止报错
Promise.resolve(obj.value).then(() => {next()})
}
next()
}
文章图片
如果说sleep1是一个网络请求的话,在yield后面就可以拿到请求返回的数据,看起来像是一种更优雅的异步问题解决方案。
如果想要拿到sleep函数resolve的值,也是可以实现的。
// 修改函数 变量接收yield语句返回结果
function* helloWorldGenerator() {
var a = yield sleep1();
console.log(a);
var b = yield sleep2();
console.log(b);
}
// g.next(v);
传递结果值
function run(generator) {
var g = generator();
var next = function(v) {
var obj = g.next(v);
console.log(obj)
if(obj.done) return;
// 如果yield后面返回值不是promise,可以使用Promise.resolve包裹一下,防止报错
Promise.resolve(obj.value).then((v) => {next(v)})
}
next()
}
【一步一步理解Generator函数的原理】你会看到和上面一样的打印结果。
仔细看上面实现方式跟async await很相似,实际上这就是async await的原理。
async await async await本质上就是结合promise实现的一个自执行Generator函数。
将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已
。更详细的代码如下,感兴趣的同学可以深入了解一下:
// async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已
// async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
// Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器和普通函数一样执行
// async函数对 Generator 函数的改进:
/*
(1)内置执行器
(2)更好的语义
(3)更广的适用性
(4)返回值是 Promise
*/
// async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args) {
// ...
}
// 等同于 里面的await 换成 yield
function fn(args) {
return spawn(function*() {
// ...
});
}function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
// 这也是为什么 不使用try catch的话,异常异步请求后面的代码不再执行的原因,从这里不再继续调用nex()方法了。
}
Promise.resolve(next.value).then(function(v) {
step(function() {
return gen.next(v);
});
}, function(e) {
step(function() {
return gen.throw(e);
// 这里可以解释为什么async 函数需要使用try catch来捕获异常,生成器函数的throw,会让代码到catch里面
});
});
}
step(function() {
return gen.next(undefined);
});
});
}
想向技术大佬们多多取经?开发中遇到的问题何处探讨?如何获取金融科技海量资源?
恒生LIGHT云社区,由恒生电子搭建的金融科技专业社区平台,分享实用技术干货、资源数据、金融科技行业趋势,拥抱所有金融开发者。
扫描下方小程序二维码,加入我们!
文章图片
推荐阅读
- 深入理解Go之generate
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- 由浅入深理解AOP
- 逻辑回归的理解与python示例
- 也许第一步很难,但跨过去就好了
- 「按键精灵安卓版」关于全分辨率脚本的一些理解(非游戏app)
- 深入理解|深入理解 Android 9.0 Crash 机制(二)
- 不理解句意,你还想做对所有GRE填空题()
- 高度自律等于成功的第一步
- 如何理解“超我、自我、本我”