异步迭代器 for-await-of

异步迭代器 for-await-of

迭代器接口(iterator)

集合概念有字符串、数组、对象、Map、Set,需要有一个统一的接口机制来处理所有不同的数据结构。

是什么
迭代器iterator是一种接口,为不同的数据结构提供统一的访问机制

好处

  • 为各种数据结构,提供一个统一的、简便的访问接口
  • 任何数据结构只要部署 Iterator 接口(该接口的值为迭代器对象),就可以完成for..of遍历操作
  • 使得数据结构的成员能够按某种次序排列

使用场景

  • for-of 遍历,Array.from创建浅拷贝数组
  • 扩展运算符(…)也会调用默认的 Iterator 接口。
  • 对数组和Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。

原理

迭代器对象
满足下属条件的对象可以称其为迭代器对象

  • 有一个next方法,每次调用next方法都将返回一个结果。结果值是一个object {value:xxx,done},value表示具体的返回值, done 是布尔类型的,表示集合是否遍历完成。
  • 内部会维护一个指针,用来指向当前集合的位置,每调用一次 next 方法,指针都会向后移动一个位置。

部署迭代器接口

  • 可迭代的数据接口内部都有[Symbol.iterator]的属性,也称为实现了Iterator接口
  • [Symbol.iterator]的属性会返回一个函数createIterator函数,创造迭代器对象的方法
    • 迭代器对象中有一个名称叫做next的方法
    • next方法每次执行都会返回一个对象{value: value, done: boolean}

部署了迭代器接口的数据结构被称为可迭代对象
String,Array,TypedArray,Map 和 Set 都部署了迭代器接口,因为它们的原型对象都有一个[Symbol.iterator]方法。

案例:创建一个迭代器对象

/*
给数据结构部署iterator接口 
[Symbol.iterator] = createIterator
*/

// createIterator方法:创建一个迭代器对象
// 如果需要实现逆序:i初始化为items.length-1,依次i--
function createIterator(items) {
  let i = 0; // 内部指针
  //迭代器对象,它具有一个 next 方法,该方法会返回一个对象,包含 value 和 done 两个属性
  return {
    next: function () {
      let done = i >= items.length;
      let val = !done ? items[i++] : undefined;
      return {
        done: done,
        value: val
      }
    }
  }
}

//测试
var it = createIterator(['a', 'b', 'c']);
console.log(it.next());// {value: "a", done: false}
console.log(it.next());// {value: "b", done: false}
console.log(it.next());// {value: "c", done: false}
console.log(it.next());// "{ value: undefined, done: true }"
console.log(it.next());// "{ value: undefined, done: true }"
console.log(it.next());// "{ value: undefined, done: true }"

异步迭代器

如果循环的是promise数组,promise的value是异步获取的,所以在当前位置是获取不到其value值。

部署异步迭代器接口
[Symbol.asyncIterator] 异步迭代器接口

function createIterator(items) {
  let i = 0; // 内部指针
  //迭代器对象,它具有一个 next 方法,该方法会返回一个对象,包含 value 和 done 两个属性
  return {
    next: function () {
      let done = i >= items.length;
      const value = done ? undefined : i++;
      return Promise.resolve({ value, done });
    }
  }
}

同步迭代器 和 异步迭代器

同步迭代器

// 迭代器对象
interface Iterator {
    next(value) : IteratorResult;
    [optional] throw(value) : IteratorResult;
    [optional] return(value) : IteratorResult;
}

// 迭代结果
interface IteratorResult {
    value : any;
    done : bool;
}

异步迭代器

// 异步迭代器
interface AsyncIterator {
    next(value) : Promise<IteratorResult>;
    [optional] throw(value) : Promise<IteratorResult>;
    [optional] return(value) : Promise<IteratorResult>;
}

// 迭代结果
interface IteratorResult {
    value : any;
    done : bool;
}

异步迭代器的遍历 for-await-of

for await of 循环可以暂停循环,当第一个异步执行完成后才会执行下一个,最后是让输出结果保持同步顺序输出。

如何循环顺序获取到promise数组中的value值

对于promise数组来说,一般按顺序获取其结果使用Promise.all(),这样每一次promise的结果只能从最后返回的数组中获取(前提是所有promise都成功)。
在实际开发中,假设每一个promise是一个请求,那么就在请求返回的地方进行一些操作。

那么如何循环顺序获取到promise数组中的value值?

// 模拟一些从后端请求获取的数据等
function Gen(time) {
  return new Promise((resolve) => {
    setTimeout(function () {
      // 模拟每个任务执行时间不一样,任务执行的结果由resolve返回。
      resolve(time);
    }, time);
  });
}

async function test() {
  const arr = [Gen(2000), Gen(1000), Gen(3000)]; //
  let i = 0;
  for (const item of arr) {
    console.log( 
       Date.now(),
       await item,  //这里的打印会放入微队列
    );
  }
}

test();
//1698219031697 2000
//1698219033701 1000
//1698219033701 3000

这种写法虽然可以得到结果,但是时间不太正确。如果把异步任务假想成一个后端请求,那么2000的结果出来的时候,1000早就执行完毕了。所以最终解决方案for-await-of

function Gen(time) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      resolve(time)
    },time)
  })
}

async function test() {
  let arr = [Gen(2000), Gen(1000), Gen(3000)]
  for await(let item of arr) {
    console.log(Date.now(), item) //item直接是promise的value值
  }
}

test()
//1698218308077 2000
//1698218308077 1000
//1698218309080 3000

//等价于
const p1 = await Gen[2000];
console.log(p1);
const p2 = await Gen[1000];
console.log(p2);
const p3 = await Gen[3000];
console.log(p3);

你可能感兴趣的:(算法,开发语言,javascript)