zoukankan      html  css  js  c++  java
  • Promise,Generator,Await/Async

    上节中忘记讲: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    //1
    y    //2

    这里有个需要特别注意的点:一旦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()]
    //2
    // [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讲太多,篇幅过长~~~


  • 相关阅读:
    【学习小记】一般图最大匹配——带花树算法
    如何检查oracle的归档空间是否满了
    Linux 的计划任务
    转 oracle的热备份和冷备份
    SQLException: Io 异常: Connection refused ERR=12514 ERR=1153异常处理过程
    查看oracle数据库版本
    ORACLE默认实例设置--linux
    oracle查看用户属于哪个表空间
    oracle默认数据库实例
    oracle 查看用户所在的表空间
  • 原文地址:https://www.cnblogs.com/tangjiao/p/9045122.html
Copyright © 2011-2022 走看看