成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

說一說 JavaScript 異步迭代器

開發 前端
異步迭代器非常強大,尤其是在Javascript等動態和異步語言中。有了它們,我們就可以將復雜的執行變成簡單的代碼,從而向用戶隱藏復雜性,增加友好的用戶體驗。

你知道嗎?除了像Promise.finally這樣的 API 之外,ECMAScript 2018還為我們帶來了另一種處理迭代器的方式——異步迭代器。

問題

假設現在我們正處于這樣一個情景:需要使用Node.js逐行讀取文件。Node有個API叫做readLine,它是一個包裝器,可用于逐行從輸入流中讀取數據,不需要分析輸入緩沖區、也不需要將文本分解為小塊。

你可以像這樣監聽:

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

reader.on('line', (line) => console.log(line))

假設有這樣一個簡單的文件:

line 1
line 2
line 3

如果我們在創建的文件上運行代碼,那么就能在控制臺上逐行輸出。但是,使用事件并不是編寫可維護代碼的最佳方法之一,因為事件是完全異步的,可能會中斷代碼流——因為是無序觸發的,并且只能通過偵聽器分配操作。

解決方案

除了事件 API之外,readline還有async iterator。現在我們可以不通過line事件中的偵聽器讀取,而是通過for關鍵字來讀取。

舉幾個使用for循環的例子。第一個是最常見的使用計數器和條件:

for (let x = 0; x < array.length; x++) {
  // Code here
}

我們也可以使用for … in表示法讀取數組索引:

const a = [1,2,3,4,5,6]
for (let index in a) {
  console.log(a[index])
}

在前一種情況下,console.log輸出從1到6的數字,但是如果我們使用console.log (index),那么記錄的是數組的索引,從0到5的數字。

下面,我們使用for … of表示法,直接獲取數組的可枚舉屬性,即它的直接值:

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

for (let item of a) {
  console.log(item)
}

注意,這些方法都是同步的。那么,如果我們有一系列promise,這時該如何按順序讀取呢?

假設我們還有另一個接口,它總是返回一個Promise。為了按順序解析promise,我們需要這樣做:

async function readLine (files) {
  for (const file of files) {
    const line = await readFile(file) // Imagine readFile is our cursor
    console.log(line)
  }
}

而現在,多虧異步可迭代對象(如readline)的魔力,我們可以執行以下操作:

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()

注意,我們現在使用的是for、for await (const x of y)的新定義。

對于await和node.js

從10.x版開始,Node.js運行時原生支持for await表示法。如果你使用的是8.x或9.x版本,則需要使用--harmony_async_iteration標志啟動Javascript文件。遺憾的是,Node.js的版本6和版本7不支持異步迭代器。

為了理解異步迭代器的概念,首先我們需要知道迭代器的本質。簡而言之,迭代器是一個對象,公開next()函數,此函數返回另一個對象,其中{value: any, done: boolean}表示當前迭代的值,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
    }
  }
}

就其本身而言,迭代器沒有實際用途,那么怎么辦呢?為此,我們需要iterable。iterable是一個對象,它有一個Symbol.iterator鍵,該鍵返回的函數返回迭代器:

// ... Iterator code here ...

const iterable = {
  [Symbol.iterator]: () => iterator
}

現在我們可以正常使用迭代器了,通過for (const x of iterable),我們可以一個一個地迭代array中的所有值。

在后臺,所有數組和對象都有Symbol.iterator,這樣就可以執行for (let x of [1,2,3])并返回我們想要的值。

我們可以看到,異步迭代器與迭代器完全相同,不同之處在于iterable擁有的是Symbol.asyncIterator,而不是Symbol.iterator,擁有的是解析為具有相同簽名對象的Promise,而不是返回{value, done}的對象。

讓我們把上面的迭代器變成一個異步迭代器:

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
}

異步迭代

我們可以通過調用next()函數來手動迭代迭代器:

// ... Async iterator Code here ...

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 }
}

為了遍歷異步迭代器,我們需要使用for await,但請記住,關鍵字await只能在異步函數中使用,所以我們需要有類似這樣的代碼:

// ... Code above ...

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

iterate() // 1, 2, 3

但是,由于Node 8.x和9.x這樣的老版本不支持異步迭代器,為了在這些版本中使用異步迭代器,我們可以簡單地從對象中提取next并手動遍歷:

// ... Async Iterator Code here ...

async function iterate () {
  const {next} = asyncIterable[Symbol.asyncIterator]() // we take the next iterator function

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

注意,for await更干凈、更簡潔,因為它的行為類似于常規循環,而且,除了更易于理解之外,還可以通過done鍵自行檢查迭代器的結束。

處理錯誤

如果promise在迭代器中被拒絕,會發生什么?好吧,和任何被拒絕的promise一樣,通過簡單的try/catch就可以來捕獲錯誤(因為我們使用的是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()

回退

關于異步迭代器,非常有趣的一點是,它們有Symbol.iterator的回退,這意味著你也可以將它與常規迭代器一起使用,例如,有這樣一個promise數組:

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

異步生成器

在大多數情況下,迭代器和異步迭代器可以創建自生成器。

生成器是允許暫停和恢復執行的函數,因此可以操作執行,然后通過next()函數獲取下一個值。

異步生成器的行為類似于異步迭代器,但你必須手動實現停止機制,例如,這里我們構建一個用于git提交的隨機消息生成器:

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

  while (true) {
    const response = await fetch(url)
    yield await response.text() // We return the value
  }
}

注意,在任何時候都不會返回{value, done}對象,因此循環無法知道執行何時完成。這時我們可以實現這樣的函數:

// Previous Code
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.

用例

再來一個更有趣的示例,為一個真實用例構建異步迭代器。目前,適用于Node.js的Oracle數據庫驅動程序支持resultSet API,因此可以在數據庫上執行查詢并返回,而數據流則可以通過getRow()方法逐個讀取。

要創建resultSet,我們需要在數據庫中執行查詢,如下所示:

const oracle = require('oracledb')
const options = {
  user: 'example',
  password: 'example123',
  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)

resultSet有一個名為getRow()的方法,這個方法從數據庫中返回要獲取的下一行的Promise。我們可以創建一個逐行返回此resultSet的光標。下面讓我們創建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

查看光標是否接收到它應該處理的resultSet,并將其存儲在當前狀態。因此,我們需要更改之前的方法,以便返回光標而不是resultSet:

const oracle = require('oracledb')
const options = {
  user: 'example',
  password: 'example123',
  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()

這樣,我們就可以遍歷所有返回的行,不需要單獨的Promise解析。

結論

異步迭代器非常強大,尤其是在Javascript等動態和異步語言中。有了它們,我們就可以將復雜的執行變成簡單的代碼,從而向用戶隱藏復雜性,增加友好的用戶體驗。

責任編輯:武曉燕 來源: 前端新世界
相關推薦

2021-06-27 21:10:12

Linux 進程控制

2021-07-31 22:20:00

線程池系統參數

2020-10-30 10:38:50

Python開發語法

2011-07-26 09:04:44

MySQL Repli數據庫負載均衡

2015-10-23 11:40:08

SaaS應用開發

2021-01-06 17:28:00

MySQL數據庫緩存池

2023-12-29 10:28:24

SPIJava靈活性

2011-07-25 13:34:08

ORACLEFLASHBACK T

2011-07-25 17:38:32

數據存儲一致性模型

2018-01-17 15:15:22

虛擬化IO半虛擬化

2018-04-09 15:10:50

測試方法新手軟件

2021-03-09 10:05:06

5G運營商技術

2023-03-13 22:09:59

JavaSpring機制

2024-10-24 08:31:26

2020-11-24 08:00:22

JavaScript對象迭代器

2010-11-17 10:20:46

求職

2021-03-04 23:12:57

Node.js異步迭代器開發

2021-09-15 06:55:34

異步LinqC#

2023-10-26 00:41:46

臟讀數據幻讀

2020-12-08 06:28:47

Node.js異步迭代器
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 天堂一区二区三区四区 | 在线观看视频福利 | 超碰免费观看 | 精品一二| 欧美精品在线视频 | 一区二区久久精品 | 精品国产一区探花在线观看 | 久久久久久久一区 | 久久久久久亚洲精品 | 国产精品第2页 | 天堂综合网久久 | 国产精品久久久久一区二区 | 精品av| 国产精品免费一区二区三区四区 | 日韩中文字幕在线视频 | 日韩视频二区 | 1区2区视频 | 在线观看av网站 | 久久国产精品视频 | 中文字幕精品一区二区三区精品 | 国产精品久久久久久久免费大片 | 永久www成人看片 | 成人av在线播放 | 久久精品国产免费 | 免费在线一区二区三区 | 男女免费视频网站 | 一级片av | 日韩aⅴ在线观看 | www.蜜桃av| 国产一级片在线播放 | 午夜精品一区二区三区在线观看 | 欧美精品二区 | 国产精品美女久久久久久久久久久 | 欧美性乱| 国产高清免费视频 | 青青草视频网 | 欧美精品在线免费观看 | 91av视频在线观看 | 99久久久无码国产精品 | 中文字幕中文字幕 | 国产精品综合 |