从C#到TypeScript - Generator
总目录
从C#到TypeScript - Generator
上篇讲了Promise
,Promise
的执行需要不停的调用then
,虽然比callback要好些,但也显得累赘。所以ES6里添加了Generator
来做流程控制,可以更直观的执行Promise,但终级方案还是ES7议案中的async await
。
当然async await
本质上也还是Generator
,可以算是Generator
的语法糖。
所以这篇先来看下Generator.
Generator语法
先来看个例子:
function* getAsync(id: string){
yield 'id';
yield id;
return 'finish';
}
let p = getAsync('123');
console.info(p.next());
console.info(p.next());
console.info(p.next());
先看下和普通函数的区别,function
后面多了一个*
,变成了function*
,函数体用到了yield
,这个大家比较熟悉,C#也有,返回可枚举集合有时会用到。
在ES6里yield
同样表示返回一个迭代器,所以用到的时候会用next()
来顺序执行返回的迭代器函数。
上面代码返回的结果如下:
{ value: 'id', done: false }
{ value: '123', done: false }
{ value: 'finish', done: true }
可以看到next()
的结果是一个对象,value
表示yield
的结果,done
表示是否真正执行完。
所以看到最后return了finish
时done
就变成true了,如果这时再继续执行next()
得到的结果是{ value: undefined, done: true }
.
Generator原理和使用
Generator
其实是ES6对协程的一种实现,即在函数执行过程中允许保存上下文同时暂停执行当前函数转而去执行其他代码,过段时间后达到条件时继续以上下文执行函数后面内容。
所谓协程其实可以看做是比线程更小的执行单位,一个线程可以有多个协程,协程也会有自己的调用栈,不过一个线程里同一时间只能有一个协程在执行。
而且线程是资源抢占式的,而协程则是合作式的,怎样执行是由协程自己决定。
由于JavaScript是单线程语言,本身就是一个不停循环的执行器,所以它的协程是比较简单的,线程和协程关系是 1:N。
同样是基于协程goroutine的go语言实现的是 M:N,要同时协调多个线程和协程,复杂得多。
在Generator
中碰到yield
时会暂停执行后面代码,碰到有next()
时再继续执行下面部分。
当函数符合Generator
语法时,直接执行时返回的不是一个确切的结果,而是一个函数迭代器,因此也可以用for...of
来遍历,遍历时碰到结果done
为true则停止。
function* getAsync(id: string){
yield 'id';
yield id;
return 'finish';
}
let p = getAsync('123');
for(let id of p){
console.info(id);
}
打印的结果是:
id
123
因为最后一个finish
的done
是true,所以for...of
停止遍历,最后一个就不会打印出来。
另外,Generator
的next()
是可以带参数的,
function* calc(num: number){
let count = yield 1 + num;
return count + 1;
}
let p = calc(2);
console.info(p.next().value); // 3
console.info(p.next().value); // NaN
//console.info(p.next(3).value); // 4
上面的代码第一个输出是yield 1 + num
的结果,yield 1
返回1,加上传进来的2,结果是3.
继续输出第二个,按正常想法,应该输出3,但是由于yield 1
是上一轮计算的,这轮碰到上一轮的yield
时返回的总是undefined
。
这就导致yield 1
返回undefined
,undefined + num返回的是NaN
,count + 1也还是NaN,所以输出是NaN
。
注释掉第二个,使用第三个就可以返回预期的值,第三个把上一次的结果3用next(3)传进去,所以可以得到正确结果。
如果想一次调用所有,可以用这次方式来递归调用:
let curr = p.next();
while(!curr.done){
console.info(curr.value);
curr = p.next(curr.value);
}
console.info(curr.value); // 最终结果
Generator
可以配合Promise
来更直观的完成异步操作。
function delay(): Promise<void>{
return new Promise<void>((resolve, reject)=>{setTimeout(()=>resolve(), 2000)});
}
function* run(){
console.info('start');
yield delay();
console.info('finish');
}
let generator = run();
generator.next().value.then(()=>generator.next());
就run
这个函数来看,从上到下执行是很好理解的,先输出'start',等待2秒,再输出'finish'。
只是执行时需要不停的使用then
,好在TJ大神写了CO模块,可以方便的执行这种函数,把Generator
函数传给co
即可。
co(run).then(()=>console.info('success'));
co的实现原理可以看下它的核心代码:
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled(); //最主要就是这个函数,递归执行next()和then()
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res); // next(), res是上一轮的结果
} catch (e) {
return reject(e);
}
next(ret); // 里面调用then,并再次调用onFulfilled()实现递归
return null;
}
function onRejected(err) { // 处理失败的情况
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
if (ret.done) return resolve(ret.value); // done是true的话表示完成,结束递归
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected); //递归onFulfilled
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}
可以看到co的核心代码和我上面写的递归调用Generator
函数的本质是一样的,不断调用下一个Promise,直到done
为true。
纵使有co这个库,但是使用起来还是略有不爽,下篇就轮到async await
出场,前面这两篇都是为了更好的理解下一篇。
从C#到TypeScript - Generator的更多相关文章
- C# vs TypeScript - 高级类型
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 变量
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 接口
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 类
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - function
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - 装饰器
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - Promise
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - async await
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- 从C#到TypeScript - Reflect
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
随机推荐
- 解决tomcat运行报错java.lang.UnsatisfiedLinkError: apache-tomcat-7.0.37\bin\tcnative-1.dll:Can load AMD 64
http://www.apache.org/dist/tomcat/tomcat-connectors/native/ 到该地址下下载一个tomcat-native-1.2.2-win32-bin压缩 ...
- C#中的协变OUT和逆变
泛型接口和泛型委托中经常使用可变性 in 逆变,out 协变 从 list<string>转到list<object> 称为协变 (string 从object 派生,那么 ...
- 解析xml的问题未解决
工作上需要解析xml,目前的逻辑是:解析xml到对象中,然后对象再插入数据库.但这存在内存溢出的风险. 今天做的另外一件事是将循环用到工作上,暂时还没有测试,是否能保证程序的重启.有待观察 ##### ...
- oracle系列--解锁数据库
一.安装完成后解锁Scott数据库步骤: 进入SQL Plus 请输入用户名:sys输入口令:sys as sysdba //这里的口令是不可见的,注意空格SQL> alter user sc ...
- 创建iwatch 程序选项
include complication :包含自定义表盘事件 include glance scene:包含缩略图事件
- Android环境搭建与HelloWorld
引言 本系列适合0基础的人员,因为我就是从0开始的,此系列记录我步入Android开发的一些经验分享,望与君共勉!作为Android队伍中的一个新人的我,如果有什么不对的地方,还望不吝赐教. 在开始A ...
- js 验证文本框只能输入数字和小数点
第一步.添加js方法 function check(e) { var re = /^\d+(?=\.{0,1}\d+$|$)/ if (e.value != "") ...
- YII 1.0 小功能总结
1.操作成功提示 只能使用一次,getFlash()取值以后,值就删除了 控制器中: Yii::app()->user->setFlash('success','修改成功'); 视图中: ...
- C,C++,VC++有什么区别
C语言是一种古老而又经久不衰的计算机程序设计语言,大约诞生于上个世纪60年代.由于它的设计有很多优点,多年以来深受广大程序设计人员的喜爱,并逐渐淘汰了很多其它程序设计语言.我们平时使用的大多数软件都是 ...
- redhat5安装phantomjs
Linux 64-bit Download phantomjs-2.1.1-linux-x86_64.tar.bz2 (22.3 MB) and extract the content. Note: ...