上节中忘记讲:Iterator接口和Generator函数的关系了,Symbol.iterator方法的最简单的实现就是通过Generator函数:

let myIterable = {
[Symbol.iterator]:function* (){
yield 1;
yield 2;
yield 3;
}
}
let aa = [...myIterable];
aa //[1, 2, 3]

用Generator函数写Symbol.iterator方法几乎不用部署任何代码,只要yield命令给出每一步的返回值即可。接下来开始写Generator函数吧,貌似没有promise好用,看下图,Await/Async函数是最腻害的,所以掌握最强的方法,你就可以所向披靡了。

在浏览知乎的中一个有关node.js是用来做什么的时候,看到有关ES6的新特性,然后就写个博客吧!
Part One:promise

Part Two:Generator

Generator函数是个状态机,封装多个内部状态。执行Generator函数会返回一个遍历器对象。

function* hello(){
yield 'hello';
yield 'world';
return 'ending';
} var hw = hello();
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函数,变量hw是一个指向hello函数内部状态的指针对象。函数的调用必须调用遍历器对象的next方法,使得指正移向下一个状态。调用next方法时候,内部指针从函数头部或者上一次停下来的地方开始执行。Generator函数是分段执行的,yield表达式是暂停执行的标记,next是恢复执行。

yield表达式:

在Generator函数内部,yield表达式只是暂停的标识。

yield和return的相似之处在于都能返回紧跟在语句后面的表达式的值,区别在于每次遇到yeild,函数暂停执行,下一次再从该位置继续向后执行。return不具备位置记忆的功能。Generator函数可以返回一系列的值,因为内部可以有多个yield。(Generator在英文中是“生成器”的意思)

如果Generator内部不用yield,就变成了一个单纯的暂缓执行的函数。

yield只能用在Generator函数内部,其他地方是会报错的!!!

在用for/of进行遍历Generator时候,不需要再调用next方法,例如最上面的例子:Generator和Iterator接口的关系时,举个栗子:

function* foo(){
yield 1;
yield 2;
yield 3;
return 4;
}
for(var i of foo()){
console.log(i)
}
//1 2 3

一旦next方法的返回对象的done属性为true时,for/of循环就会中止,且不包含返回值,所以上面的return语句返回的4不包含在循环中。

这里使用Generator函数写了一个斐波那契数列的方法,遍历时候将小于1000的数字打印出来:

function* fibonacci(){
let [prev,curr] = [0,1];
for(;;){
[prev,curr] = [curr,prev+curr];
yield curr;
}
}
for(let n of fibonacci()){
if(n>1000) break;
console.log(n);
}

如何遍历一个没有Iterator接口的对象呢?上一节中给对象加上Symbol.iterator属性就可以,而在Generator当中呢?

function* objectEntires(obj){
let propKeys = Reflect.ownKeys(obj); for(let propKey of propKeys){
yield [propKey,obj[propKey]];
}
} let jane = {first: 'Jane',last: 'Ostin'};
for(let [key,value] of objectEntires(jane)){
console.log(`${key}->${value}`)
}
VM4211:11 first->Jane
VM4211:11 last->Ostin 或者
let jane = {first: 'Jane',last: 'Ostin'};
function* objectEn(){
    let propKeys = Object.keys(this);
    for(let propKey of propKeys){
       yield [propKey,this[propKey]];
    }
}
jane[Symbol.iterator] = objectEn;
for(let [key,value] of jane){
    console.log(`${key}->${value}`)
}

函数当中涉及到Reflect,有时间写一篇有关Reflect的博文~~~

拓展运算符(...),解构赋值和Array.from()等都是遍历器接口,可以将Generator函数作为参数。

function* numbers(){
yield 1;
yield 2;
return 3;
yield 4;
}
let aa = [...numbers()]
let bb = Array.from(numbers());
let [x,y] = numbers(); aa //[1, 2]
bb //[1, 2]
x //
y //

这里有个需要特别注意的点:一旦Generator执行过程中抛出错误,且没有被内部捕获,就不会再执行下去了,如果还调用next方法,value就是undefined,done就是true了。即JavaScript引擎认为这个Generator已经运行结束了。

注意:在Generator内部调用一个Generator函数是不会起作用的,所以就需要用到yield*表达式,用于在一个Generator函数 里面执行另外一个Generator函数。

function* foo(){
yield 'a';
yield 'b';
} function* bar(){
yield 'x';
yield* foo();
yield 'y';
}
[...bar()] //["x", "a", "b", "y"]

如果不用yield* 而使用yield后面跟一个Generator函数的话,返回的就是一个遍历器对象了。在使用yield*时候(Generator里面没有return语句),就类似于for/of的简写形式。在有return语句时候呢?
任何数据结构只要有Iterator接口,就可以被yield*遍历。

let read = (function* (){
yield 'hello';
yield* 'hello';
}())
[...read] //["hello", "h", "e", "l", "l", "o"]
function* AA(){
yield 1;
return 2;
}
function* BB(){
yield 3;
let re = yield* AA();
console.log(re)
yield 4;
}
[...BB()]
//
// [3, 1, 4]

在运行内部有return的Generator函数时候,如果将它赋值为一个变量指针,则打印结果是会有return的结果输出。

出个题:如果遍历二维数组?

const tree = ['a',['b','c'],['d','e']];
function* iterTree(arr){
for(let item of arr){
if(Array.isArray(item)){
yield* iterTree(item);
}else{
yield item;
}
}
}
[...iterTree(tree)] // ["a", "b", "c", "d", "e"]

如果对象的属性是Generator函数,可以进行简写:

let obj = {
* myMethod(){
yield 1;
yield 2;
}
}
[...obj.myMethod()] //[1, 2] 类似于==》
let obj = {
myMethod:function* (){
yield 1;
yield 2;
}
}
[...obj.myMethod()] //[1, 2]

注意:Generator函数和普通构造函数的区别?

相同点:实例化后的Generator函数对象,会继承Generator函数的原型上的方法,这点跟构造函数蕾西。

区别:Generator函数返回的是遍历器对象,而不是this对象,所以函数上的固有方法,实例是不继承的,也不能new一个Generator对象,会报错。

如何将Generator函数返回一个正常的实例对象,既可以使用next方法,也可以获得正常的this对象?例如:F是一个Generator函数,可以将F内部的this对象绑定obj对象,然后调用next方法,返回Iterator对象,给obj上面添加属性,将所有内部的属性都绑定在obj对象上面,因此obj对象也就是F的实例了。

如何将obj和f统一?

可以将F的this指向它自己的原型对象。如下:

function* F(){
this.a = 1;
yield this.b = 2;
yield this.c = 3;
} var f = F.call(F.prototype);
f.next();
f.next();
f.next();
console.log(f.a,f.b,f.c); //1,2,3

就是将Generator函数里面的this指向了它本身的原型对象上面,在调用了next方法之后给原型上面添加属性。

如何让f是可以用构造函数new出来的对象,还可以使用Generator函数的next方法?

function* F(){
this.a = 1;
yield this.b = 2;
yield this.c = 3;
} function gen(){
return F.call(F.prototype);
}
var f = new gen();
f.next();
f.next();
f.next();
console.log(f.a,f.b,f.c);
VM910:14 1 2 3

就是真正的再新建一个构造函数,里面return F的实例,然后就可以new一个对象并使用Generator的方法,还有原型上面的属性了。

用Generator函数 实现一个状态机就更加简洁化,而且比较安全(状态不会给非法篡改),更加符合函数式编程的思想。

var clock = function* (){
while(true){
console.log("Tick!");
yield;
console.log("Tock!")
yield;
}
}
var c = clock();
c.next() //Tick!
c.next() //Tock!
c.next() //Tick!

Generator可以暂停函数执行,返回任意表达式的值,应用场景:

1,处理异步操作,改写回调函数

2,可以通过Generator函数逐步读取文本文件

3,控制流管理,多部操作非常耗时,采用回调函数的方式,函数嵌套的模式。如果是promise的话,可能是回调在then里面的嵌套,如果用Generator函数的话:

function scheduler(task){
var taskObj = task.next(task.value);
if(!taskObj.done){
task.value = taskObj.value;
scheduler(task);
}
}
function* longRunningTask(v1){
try{
var v2 = yield step1(v1);
var v3 = yield step2(v2);
var v4 = yield step3(v3);
var v5 = yield step4(v4);
}catch(e){
console.log(`ERR${e}`)
}
} scheduler(longRunningTask(initialValule))

那么异步的处理方法呢??下一节讲Await/Async方法,本节的Generator讲太多,篇幅过长~~~

Promise,Generator,Await/Async的更多相关文章

  1. javascript异步编程的前世今生,从onclick到await/async

    javascript与异步编程 为了避免资源管理等复杂性的问题, javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是为 ...

  2. 5分种让你了解javascript异步编程的前世今生,从onclick到await/async

      javascript与异步编程 为了避免资源管理等复杂性的问题,javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是 ...

  3. 异步解决方案----Promise与Await

    前言 异步编程模式在前端开发过程中,显得越来越重要.从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题.随着ES6新标准的到来,处理异步数据流又有了新的方案.我们都知道,在传统的aja ...

  4. Promise, Generator, async/await的渐进理解

    作为前端开发者的伙伴们,肯定对Promise,Generator,async/await非常熟悉不过了.Promise绝对是烂记于心,而async/await却让使大伙们感觉到爽(原来异步可以这么简单 ...

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

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

  6. Promise、Generator,Async/await

    我们知道JavaScript是单线程语言,如果没有异步编程非得卡死. 以前,异步编程的方法有下面四种 回调函数 事件监听 发布/订阅 Promise对象 现在据说异步编程终极解决方案是——async/ ...

  7. ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await

    ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await co ...

  8. Generator与async/await与Generator的模拟

    Generator 函数有多种理解角度.语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态. 执行 Generator 函数会返回一个遍历器对象,也就是说,Gener ...

  9. js中异步方案比较完整版(callback,promise,generator,async)

    JS 异步已经告一段落了,这里来一波小总结 1. 回调函数(callback) setTimeout(() => { // callback 函数体 }, 1000) 缺点:回调地狱,不能用 t ...

随机推荐

  1. Spring Boot Admin最佳实践

    本文不进行Spring Boot Admin入门知识点说明 在Spring Boot Actuator中提供很多像health.metrics等实时监控接口,可以方便我们随时跟踪服务的性能指标.Spr ...

  2. python运行报错——注释报错

    本人是IT行业的,从事软件测试,还是个菜鸟.希望大神们多多关照~ 首先,开通这个博客的目的: 1)通常我容易犯一些低级的错误,而且在网上找到解决方法,解决之后时间长了又不记得: 2)想和有共同兴趣的人 ...

  3. vue项目中net::ERR_CONNECTION_TIMED_OUT错误

    我出错的原因时network地址与我本机ip地址不一致 Network: http://192.168.13.30:8080/ 处理方法: 在vue项目中新建一个vue.config.js文件 配置上 ...

  4. 关于分频器的FPGA实现整理思路

    分频器是用的最广的一种FPGA电路了,我最初使用的是crazybingo的一个任意分频器,可以实现高精度任意分频的一个通用模块,他的思想在于首先指定计数器的位宽比如32位,那么这个计数器的最大值就是2 ...

  5. 2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 2) P-Fear Factoring 区间内数的所有因数的和(除法分块)

    题意就是标题. 思路: 对于每个数 a 算出 1~a 的所有因数和sum(a),输出sum(b)-sum(a-1). 关键在于如何求出 sum. 首先发现因数∈ 1 ≤ i ≤ n ,每个因数在区间[ ...

  6. C++中的大数乘的实现

    代码 来源:面试常考 大数加减乘除 #include <iostream> #include <vector> #include <string> using na ...

  7. CDC与HDC的区别以及相互转换

    CDC是MFC的DC的一个类  HDC是DC的句柄,API中的一个类似指针的数据类型.  MFC类的前缀都是C开头的  H开头的大多数是句柄  这是为了助记,是编程读\写代码的好的习惯.  CDC中所 ...

  8. 用VS2013进行调试

    首先新建一个简单的C++程序 打开VS2013-文件-新建-项目-选择Win32 控制台程序 添加-新建项-选择C++源文件 编写如下代码 #include<iostream> using ...

  9. 极力推荐大佬的java项目的博客

    此次说明:这是我从码云上拉取下来的java项目,仅供试验说明,不做任何获利渠道,若是发现有人拿此做其他用处,需像码云上这位大佬说明,特此感谢!!! 准备工作:     安装 maven,jdk1.8, ...

  10. Memcached笔记——(四)应对高并发攻击

    近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源.他们的最好成绩,1秒钟可以并发6次,赶在Database入库前,Cache进行Mis ...