美文网首页
Promise Iterator Generator asyn

Promise Iterator Generator asyn

作者: _咻咻咻咻咻 | 来源:发表于2021-02-03 16:07 被阅读0次

    Promise Iterator Generator async-await 都是异步解决方案,Iterator Generator async-await之间有千丝万缕的联系,起初从Iterator开始向后理解,感觉不好理解,于是我觉得倒着理解,从常用的async-await到其背后演变过程。
    全文参考阮一峰老师的ES6 入门教程

    一、Promise

    Promise比较简单,基本用法如下:

    const promise = new Promise((resolve, reject) => {
      if(....){
        resolve(value)
      }else{
        reject(error)
      }
    })
    promise.then(() => {
      // 成功走这里
    }).catch(() => {
      // 失败走这里
    }).finallly(() => {
      // 无论最后状态如何,都会执行
    })
    
    Promise.resolve()
    Promise.reject()
    const p = Promise.all([p1, p2, p3]);
    p.then(function (posts) {
      // ...
    }).catch(function(reason){
      // ...
    });
    
    const p = Promise.race([p1, p2, p3]);
    

    Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。p1、p2、p3都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
    p的状态由p1、p2、p3决定,分成两种情况。
    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。但是,如果p1被rejected,而且p1有自己的catch方法,则p不会走catch,此时p1执行catch后会变成resolved
    Promise.race()则是p1,p2,p3中其中一个状态改变,p的状态就改变,那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

    二、async-await

    async函数是Generator 函数的语法糖。async定义一个异步函数,这个函数内用await来调用其他异步函数,拿到结果,再继续执行下方代码。

    const fs = require('fs');
    
    const readFile = function (fileName) {
      return new Promise(function (resolve, reject) {
        fs.readFile(fileName, function(error, data) {
          if (error) return reject(error);
          resolve(data);
        });
      });
    };
    
    const asyncReadFile = async function () {
      const f1 = await readFile('/etc/fstab');
      const f2 = await readFile('/etc/shells');
      console.log(f1.toString());
      console.log(f2.toString());
    };
    

    async函数的返回值是 Promise 对象。async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
    为防止await后的异步方法出错,可以使用try{}catch{}的结构。
    知道了async函数是Generator 函数的语法糖。那么Generator是什么呢?

    三、Generator(生成器)

    上方的例子用Generator写是这样的:

    const gen = function* () {
      const f1 = yield readFile('/etc/fstab');
      const f2 = yield readFile('/etc/shells');
      console.log(f1.toString());
      console.log(f2.toString());
    };
    

    一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

    Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象,Generator 函数还是一个遍历器生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

    function* helloWorldGenerator() {
      yield 'hello';
      yield 'world';
      return 'ending';
    }
    var hw = helloWorldGenerator();
    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 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)。yield表达式是暂停标志。必须调用遍历器对象的next方法,使得指针移向下一个状态。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

    形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

    与 Iterator 接口的关系

    任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。
    由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

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

    Generator 函数执行后,返回一个遍历器对象。该对象本身也具有Symbol.iterator属性,执行后返回自身.

    function* gen(){
      // some code
    }
    var g = gen();
    g[Symbol.iterator]() === g
    // true
    
    async函数对 Generator 函数的改进。
    • 内置执行器。async函数的执行,与普通函数一样,但Generator 函数,需要调用next方法,或者用co模块,才能真正执行。
    • 更好的语义。async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
    • 更广的适用性。co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值。
    • 返回值是 Promise。async函数的返回值是 Promise 对象, Generator 函数的返回值是 Iterator。async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
    for...of 循环

    for...of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法。return语句返回的值,不包括在for...of循环之中。

    function* foo() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
      yield 5;
      return 6;
    }
    for (let v of foo()) {
      console.log(v);
    }
    // 1 2 3 4 5
    

    前面提到,generator函数返回的是一个遍历器对象(Iterator Object),那么Iterator又是什么?

    四、Iterator(遍历器)

    概念

    遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
    Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

    var it = makeIterator(['a', 'b']);
    
    it.next() // { value: "a", done: false }
    it.next() // { value: "b", done: false }
    it.next() // { value: undefined, done: true }
    
    function makeIterator(array) {
      var nextIndex = 0;
      return {
        next: function() {
          return nextIndex < array.length ?
            {value: array[nextIndex++], done: false} :
            {value: undefined, done: true};
        }
      };
    }
    

    上面代码定义了一个makeIterator函数,它是一个遍历器生成函数,作用就是返回一个遍历器对象。它的执行过程是:

    1. 创建一个指针对象指向当前数据结构的其实位置。(遍历器对象本质上,就是一个指针对象。)
    2. 调用next()将指针指向第一个成员。继续不断调用next(),指向后续成员,知道指向最后一个成员。
    默认 Iterator 接口

    ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。

    let arr = ['a', 'b', 'c'];
    let iter = arr[Symbol.iterator]();
    
    iter.next() // { value: 'a', done: false }
    iter.next() // { value: 'b', done: false }
    iter.next() // { value: 'c', done: false }
    iter.next() // { value: undefined, done: true }
    
    调用 Iterator 接口的场合
    • 解构赋值。let [first, ...rest] = ['a', 'b', 'c'];// first='a'; rest=['b','c'];
    • 扩展运算符。 var str = 'hello'; [...str] // ['h','e','l','l','o']
    • yield*。 generator函数中yield * [1, 2, 3]
    for...of循环

    ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for...of循环,作为遍历所有数据结构的统一的方法。
    一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。

    let arr = [3, 5, 7];
    arr.foo = 'hello';
    
    for (let i in arr) {
      console.log(i); // "0", "1", "2", "foo"
    }
    
    for (let i of arr) {
      console.log(i); //  "3", "5", "7"
    }
    

    上面代码表明,for...in循环读取键名,for...of循环读取键值。

    相关文章

      网友评论

          本文标题:Promise Iterator Generator asyn

          本文链接:https://www.haomeiwen.com/subject/sbfctltx.html