发布于 2026-01-06 1 阅读
0

Javascript:理解异步迭代器

Javascript:理解异步迭代器

Há um tempo atrás, fiz um post em meu Medium onde falo tudo sobre o 协议迭代器和使用接口。作为 API 的一部分Promise.finally,ECMAScript 2018 为我们的迭代器提供了一个全新的形式。操作系统异步迭代器

问题

Vamos nos colocar em uma situação bastante comum。 Node.js 和 arquivo 的工作原理,林哈林哈。 O Node possui uma API para este pointo de função chamada readLine(veja a documentação completa aqui),esta API é umwrapper para que você possa ler bados de umastream de entrada linha a linha ao invés de ter fazer or parsing do buffer de entrada e quebrar o texto em pequenas parts.

如果您使用 API 事件,则可以获取以下形式:

const fs = require('fs')
const readline = require('readline')
const reader = readline.createInterface({
  input: fs.createReadStream('./arquivo.txt'),
  crlfDelay: Infinity
})

reader.on('line', (line) => console.log(line))
Enter fullscreen mode Exit fullscreen mode

想象一下 que tenhamos um arquivo simples:

linha 1
linha 2
linha 3
Enter fullscreen mode Exit fullscreen mode

Se rodarmos este codigo no arquivo que criamos, teremos um output linha a linha no nosso console. Porém, trabalhar com events no é uma das melhores formas de fazer codigo manutenível, pois eventos são completamente assíncronos and eles podem quebrar or Fluxo do codigo, uma vez que es são disparados fora de order and você só consegue atribuir uma ação através de um 听众。

解决方案

Além da API de eventos, o readlinetambém expõe um async iterator。这是很重要的,它可以让听众在任何情况下都可以使用关键字的line新形式来播放音频for

常见的重复操作for、主要模型、使用条件和条件:

for (let x = 0; x < array.length; x++) {
  // Código aqui
}
Enter fullscreen mode Exit fullscreen mode

使用for … in数组索引的注意事项:

const a = [1,2,3,4,5,6]

for (let index in a) {
  console.log(a[index])
}
Enter fullscreen mode Exit fullscreen mode

没有前面的,vamos ter como saída no console.log,os números de 1 a 6,porém se utilizarmos console.log(index)vamos logar o índice do array,ou seja,os números de 0 a 5。

Para o próximo caso, podemos usar a notação for … ofpara pegar diretetamente as propriedades enumeráveis do array, or seja, seus valores diretos:

const a = [1,2,3,4,5,6]

for (let item of a) {
  console.log(item)
}
Enter fullscreen mode Exit fullscreen mode

知道如何使用 síncronas 的形式,或者,如何,如何 possamos ler uma sequencia de 承诺 em ordem?,想象一下,tenhamos uma 外接口,没有返回 semper uma Promise,que se 解决了 nossa linha do arquivo em问题。 Parasolvermos essas 承诺 em ordem, temos de fazer algo assim:

async function readLine (files) {
  for (const file of files) {
    const line = await readFile(file) // Imagine que readFile é o nosso cursor
    console.log(line)
  }
}
Enter fullscreen mode Exit fullscreen mode

Porém,graças a magic dos async iterables(como o readline)nós podemos fazer o seguinte:

const fs = require('fs')
const readline = require('readline')
const reader = readline.createInterface({
  input: fs.createReadStream('./xpto.txt'),
  crlfDelay: Infinity
})

async function read () {
  for await (const line of reader) {
    console.log(line)
  }
}

read()
Enter fullscreen mode Exit fullscreen mode

市场上有新的定义forfor await (const x of y)

对于等待 Node.js

Node.js 10.x版本for await不支持运行时。您可以使用 8.x 或 9.x 版本来指定 Javascript 标志--harmony_async_iteration。 Infelizmente os 异步迭代器不支持 Node.js 的 6 或 7 版本。

迭代器

关于异步迭代器的概念,请简要回顾一下迭代器。前面的内容主要包括信息、内容、迭代器和迭代器的功能,它会next()返回对象的功能,并{value: any, done: boolean}说明value迭代的实际价值和done识别顺序。举个简单的例子,数组中待办事项的迭代器:

const array = [1,2,3]
let index = 0

const iterator = {
  next: () => {
    if (index >= array.length) return { done: true }
    return {
      value: array[index++],
      done: false
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Sozinho,我的迭代器在实际应用中,可以证明我的算法是正确的iterable。 Umé iterableum objeto que possui uma chave Symbol.iteratorque retorna uma função, a qual retorna nosso iterador:

// ... Código do iterador aqui ...

const iterable = {
    [Symbol.iterator]: () => iterator
}
Enter fullscreen mode Exit fullscreen mode

Agora Podemos utilizar ele normalmente, come for (const x of iterable)to teremos todos os valores do arraysendo iterador um a um.

Se você quiser saber um pouco mais sobre Symbols , dê uma olhadaneste outro artigo que escrevi só sobre o tema

对于所有全景、所有阵列和对象,我们都会对它们进行重新Symbol.iterator设置for (let x of [1,2,3])

异步迭代器

例如,异步迭代器和迭代器的扩展,可以在迭代器中Symbol.iterator执行,并且可以Symbol.asyncIterator在可迭代的对象中执行,并且{value, done}可以通过 Promise 来解决对象的问题。

Vamos 变换 nosso 迭代​​器 acima em um 异步迭代器:

const array = [1,2,3]
let index = 0

const asyncIterator = {
  next: () => {
    if (index >= array.length) return Promise.resolve({done: true})
    return Promise.resolve({value: array[index++], done: false})
  }
}

const asyncIterable = {
  [Symbol.asyncIterator]: () => asyncIterator
}
Enter fullscreen mode Exit fullscreen mode

Iterando assíncronamente

可以使用手册中的迭代器进行迭代,并使用以下函数next()

// ... Código do async iterator aqui ...

async function manual () {
    const promise = asyncIterator.next() // Promise
  await p // Object { value: 1, done: false }
  await asyncIterator.next() // Object { value: 2, done: false }
  await asyncIterator.next() // Object { value: 3, done: false }
  await asyncIterator.next() // Object { done: true }
}
Enter fullscreen mode Exit fullscreen mode

异步迭代器的迭代器、使用方法、使用方法、使用关键字的顺序、for await或使用的await方法async function

// ... Código acima omitido ...

async function iterate () {
  for await (const num of asyncIterable) console.log(num) 
}

iterate() // 1, 2, 3
Enter fullscreen mode Exit fullscreen mode

大多数情况下,与迭代器一样,不支持 Node 8.x 或 9.x,使用异步迭代器的方式与其他版本一样,不支持next任何对象和手册中的迭代器:

// ... Código do async iterator aqui ...

async function iterate () {
  const {next} = asyncIterable[Symbol.asyncIterator]() // pegamos a função next do iterator

  for (let {value, done} = await next(); !done; {value, done} = await next()) {
    console.log(value)
  }
}
Enter fullscreen mode Exit fullscreen mode

for await所有的清洁剂和所有的清洁剂一起放入循环中,然后再将其放入循环中,然后将其放入最后的循环中,然后将其取出done

Tratando erros

O que acontece se nossapromise for rejeitada dentro do nosso iterador? Bem,como qualquer 承诺 rejeitada,podemos pegar seu erro através de um simples try/catch(já que estamos usando await):

const asyncIterator = { next: () => Promise.reject('Error') }
const asyncIterable = { [Symbol.asyncIterator]: () => asyncIterator }

async function iterate () {
  try {
      for await (const num of asyncIterable) {}
  } catch (e) {
    console.log(e.message)
  }
}

iterate()
Enter fullscreen mode Exit fullscreen mode

备用方案

异步迭代器的有趣之处在于它可以作为后备参数Symbol.iterator,这对于使用迭代器来说非常重要,例如,承诺数组:

const fetch = require('node-fetch')
const promiseArray = [
  fetch('https://lsantos.dev'),
  fetch('https://lsantos.me')
]

async function iterate () {
  for await (const response of promiseArray) console.log(response.status)
}

iterate() // 200, 200
Enter fullscreen mode Exit fullscreen mode

异步生成器

重要的是,迭代器和异步迭代器是生成器的一部分。发电机可以在暂停和重新启动时执行,以便可以实现执行并在公共汽车上发挥作用next()

这是对生成器的简单描述,是一种可以快速且深入地处理生成器的方法。

异步生成器与异步迭代器一起使用,可以实现手动操作的机制,例如,vamos 可以构建 git 中的各种提交,并提供以下贡献:

const fetch = require('node-fetch')
async function* gitCommitMessageGenerator () {
  const url = 'https://whatthecommit.com/index.txt'

  while (true) {
    const response = await fetch(url)
    yield await response.text() // Retornamos o valor
  }
}
Enter fullscreen mode Exit fullscreen mode

如果你想恢复目标{value, done},就必须执行完毕。 Podemos 实现了以下功能:

// Código anterior omitido
async function getCommitMessages (times) {
  let execution = 1
  for await (const message of gitCommitMessageGenerator()) {
    console.log(message)
    if (execution++ >= times) break
  }
}

getCommitMessages(5)
// I'll explain this when I'm sober .. or revert it
// Never before had a small typo like this one caused so much damage.
// For real, this time.
// Too lazy to write descriptive message
// Ugh. Bad rebase.
Enter fullscreen mode Exit fullscreen mode

使用案例

作为一个有趣的示例,我们在实际应用中构建了异步迭代器。实际上,Oracle 数据库驱动程序支持 Node.js API resultSet,可以执行查询银行数据并返回注册信息流和方法getRow()

Para criarmos esse resultSetprecisamos executar uma query no banco, desta forma:

const oracle = require('oracledb')
const options = {
  user: 'usuario',
  password: 'senha',
  connectString: 'string'
}

async function start () {
  const connection = await oracle.getConnection(options)
  const { resultSet } = await connection.execute('query', [], { outFormat: oracle.OBJECT, resultSet: true })
  return resultSet
}

start().then(console.log)
Enter fullscreen mode Exit fullscreen mode

Nosso resultSetpossui um método chamado getRow()que nos retorna uma Promise da próxima linha do banco que deve ser trazida, isso é um belo caso de uso para um async iterator não é? Podemos criar um 光标 que nos retorna este resultSetlinha por linha。 Vamos deixar um pouco mais Complexo através da criação de uma classe Cursor:

class Cursor {
  constructor (resultSet) {
    this.resultSet = resultSet
  }

  getIterable () {
    return {
      [Symbol.asyncIterator]: () => this._buildIterator()
    }
  }

  _buildIterator () {
    return {
      next: () => this.resultSet.getRow().then((row) => ({ value: row, done: row === undefined }))
    }
  }
}

module.exports = Cursor
Enter fullscreen mode Exit fullscreen mode

按下鼠标按钮,然后resultSet将鼠标移至实际位置。改变光标前部旋转的方法resultSet

const oracle = require('oracledb')
const options = {
  user: 'usuario',
  password: 'senha',
  connectString: 'string'
}

async function getResultSet () {
  const connection = await oracle.getConnection(options)
  const { resultSet } = await connection.execute('query', [], { outFormat: oracle.OBJECT, resultSet: true })
  return resultSet
}

async function start() {
  const resultSet = await getResultSet()
  const cursor = new Cursor(resultSet)

  for await (const row of cursor.getIterable()) {
    console.log(row)
  }
}

start()
Enter fullscreen mode Exit fullscreen mode

Desta forma Podemos fazer um Loop por todas as nossas linhas retornadas sem precisar de uma resolução de Promises individual.

结论

异步迭代器是一种极端的迭代器,特别是在恐龙语言和 Javascript 中,可以将复杂的执行转换为简单的代码,从而实现最复杂的操作。

您可以继续阅读我的博客添加新闻通讯来接收最新消息!

文章来源:https://dev.to/_staticvoid/entendendo-async-iterators-1opo