异步迭代器 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);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript 中,使用 `for..of` 或 `forEach` 循环来遍历一个数组或一个类数组对象时,是同步地进行的。如果在循环体内部调用了异步操作(例如调用接口),则会出现问题,因为循环不会等待异步操作完成,就直接执行下一次循环,导致结果不正确。 解决这个问题的方法是使用 `for await..of` 循环,它可以用于遍历支持异步迭代的对象,例如异步生成器和异步迭代器。使用 `for await..of` 循环可以确保在每次循环中等待异步操作完成后再执行下一次循环。 以下是使用 `for await..of` 循环调用接口的示例代码: ```javascript async function fetchData() { const urls = ['api/url1', 'api/url2', 'api/url3']; for await (const data of getData(urls)) { console.log(data); } } async function* getData(urls) { for (const url of urls) { const response = await fetch(url); const data = await response.json(); yield data; } } ``` 在上面的代码中,`fetchData` 函数调用了 `getData` 函数,并使用 `for await..of` 循环遍历异步生成器返回的数据。`getData` 函数接收一个 URL 数组,并使用 `for..of` 循环遍历 URL,调用 `fetch` 方法获取数据,然后使用 `yield` 关键字返回数据给异步生成器。在 `fetchData` 函数中,`for await..of` 循环等待异步生成器返回数据,并输出到控制台。这样就可以确保在每次循环中等待异步操作完成后再执行下一次循环,避免了使用 `forEach` 循环时出现的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值