hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 前端开发 - 正文 君子好学,自强不息!

深入研究Node.js中的异步生成器和异步迭代_WEB前端开发

2020-09-19前端开发搜奇网53°c
A+ A-

生成器函数在 JavaScript 中的涌现早于引入 async/await,这意味着在建立异步生成器(一直返回 Promise 且可以 await 的生成器)的同时,还引入了很多须要注重的事项。

本日,我们将研讨异步生成器及其嫡亲——异步迭代。

注重:只管这些观点应当适用于一切遵照当代范例的 javascript,但本文中的一切代码都是针对 Node.js 10、12和 14 版开发和测试的。

视频教程引荐:node js教程

异步生成器函数

看一下这个小程序:

// File: main.js
const createGenerator = function*(){
  yield 'a'
  yield 'b'
  yield 'c'
}

const main = () => {
  const generator = createGenerator()
  for (const item of generator) {
    console.log(item)
  }
}
main()

这段代码定义了一个生成器函数,用该函数建立了一个生成器对象,然后用 for ... of 轮回遍历该生成器对象。相称规范的东西——只管你毫不会在现实工作顶用生成器来处置惩罚云云噜苏的事变。假如你不熟悉生成器和 for ... of 轮回,请看《Javascript 生成器》 和 《ES6 的轮回和可迭代对象的》 这两篇文章。在运用异步生成器之前,你须要对生成器和 for ... of 轮回有踏实的相识。

假定我们要在生成器函数中运用 await,只需须要用 async 关键字声明函数,Node.js 就支撑这个功用。假如你不熟悉异步函数,那末请看 《在当代 JavaScript 中编写异步使命》一文。

下面修正程序并在生成器中运用 await

// File: main.js
const createGenerator = async function*(){
  yield await new Promise((r) => r('a'))
  yield 'b'
  yield 'c'
}

const main = () => {
  const generator = createGenerator()
  for (const item of generator) {
    console.log(item)
  }
}
main()

同样在现实工作中,你也不会如许做——你大概会 await 来自第三方 API 或库的函数。为了能让人人轻松控制,我们的例子只管坚持简朴。

假如尝试运转上述程序,则会碰到问题:

$ node main.js
/Users/alanstorm/Desktop/main.js:9
  for (const item of generator) {
                     ^
TypeError: generator is not iterable

JavaScript 通知我们这个生成器是“不可迭代的”。乍一看,好像使生成器函数异步也意味着它生成的生成器是不可迭代的。这有点令人困惑,由于生成器的目标是生成“以编程体式格局”可迭代的对象。

接下来搞清楚究竟发生了什么。

搜检生成器

假如你看了 Javascript 生成器这篇文章 ,那末就应当晓得,假如对象定义了 Symbol.iterator 要领,而且该要领返回,则它在 javascript 中是一个完成了迭代器协定的可迭代对象。当对象具有 next 要领时,该对象将完成迭代器协定,而且该 next 要领返回带有 value 属性,done 属性之一或同时带有 valuedone 属性的对象。

假如用下面这段代码比较异步生成器函数与通例生成器函数返回的生成器对象:

// File: test-program.js
const createGenerator = function*(){
  yield 'a'
  yield 'b'
  yield 'c'
}

const createAsyncGenerator = async function*(){
  yield await new Promise((r) => r('a'))
  yield 'b'
  yield 'c'
}

const main = () => {
  const generator = createGenerator()
  const asyncGenerator = createAsyncGenerator()

  console.log('generator:',generator[Symbol.iterator])
  console.log('asyncGenerator',asyncGenerator[Symbol.iterator])
}
main()

则会看到,前者没有 Symbol.iterator 要领,而后者有。

$ node test-program.js
generator: [Function: [Symbol.iterator]]
asyncGenerator undefined

这两个生成器对象都有一个 next 要领。假如修正测试代码来挪用这个 next 要领:

// File: test-program.js

/* ... */

const main = () => {
  const generator = createGenerator()
  const asyncGenerator = createAsyncGenerator()

  console.log('generator:',generator.next())
  console.log('asyncGenerator',asyncGenerator.next())
}
main()

则会看到另一个问题:

$ node test-program.js
generator: { value: 'a', done: false }
asyncGenerator Promise { <pending> }

为了使对象可迭代,next 要领须要返回带有 valuedone 属性的对象。一个 async 函数将老是返回一个 Promise 对象。这个特征会带到用异步函数建立的生成器上——这些异步生成器一直会 yield 一个 Promise 对象。

这类行动使得 async 函数的生成器没法完成 javascript 迭代协定。

异步迭代

荣幸的是有方法处理这个抵牾。假如看一看 async 生成器返回的组织函数或类

// File: test-program.js
/* ... */
const main = () => {
  const generator = createGenerator()
  const asyncGenerator = createAsyncGenerator()

  console.log('asyncGenerator',asyncGenerator)
}

可以看到它是一个对象,其范例或类或组织函数是 AsyncGenerator 而不是 Generator

asyncGenerator Object [AsyncGenerator] {}

只管该对象有大概不是可迭代的,但它是异步可迭代的。

要想使对象可以异步迭代,它必需完成一个 Symbol.asyncIterator 要领。这个要领必需返回一个对象,该对象完成了异步版本的迭代器协定。也就是说,对象必需具有返回 Promisenext 要领,而且这个 promise 必需终究剖析为带有 donevalue 属性的对象。

一个 AsyncGenerator 对象满足一切这些前提。

这就留下了一个问题——我们如何才能遍历一个不可迭代但可以异步迭代的对象?

for await … of 轮回

只用生成器的 next 要领就可以手动迭代异步可迭代对象。 (注重,这里的 main 函数现在是 async main ——如许可以使我们在函数内部运用 await

// File: main.js
const createAsyncGenerator = async function*(){
  yield await new Promise((r) => r('a'))
  yield 'b'
  yield 'c'
}

const main = async () => {
  const asyncGenerator = createAsyncGenerator()

  let result = {done:false}
  while(!result.done) {
    result = await asyncGenerator.next()
    if(result.done) { continue; }
    console.log(result.value)
  }
}
main()

然则,这不是最直接的轮回机制。我既不喜好 while 的轮回前提,也不想手动搜检 result.done。别的, result.done 变量必需同时存在于内部和外部块的作用域内。

荣幸的是大多数(也许是一切?)支撑异步迭代器的 javascript 完成也都支撑特别的 for await ... of 轮回语法。比方:

const createAsyncGenerator = async function*(){
  yield await new Promise((r) => r('a'))
  yield 'b'
  yield 'c'
}

const main = async () => {
  const asyncGenerator = createAsyncGenerator()
  for await(const item of asyncGenerator) {
    console.log(item)
  }
}
main()

假如运转上述代码,则会看到异步生成器与可迭代对象已被胜利轮回,而且在轮回体中得到了 Promise 的完整剖析值。

$ node main.js
a
b
c

这个 for await ... of 轮回更喜好完成了异步迭代器协定的对象。然则你可以用它遍历任何一种可迭代对象。

for await(const item of [1,2,3]) {
    console.log(item)
}

当你运用 for await 时,Node.js 将会首先在对象上寻觅 Symbol.asyncIterator 要领。假如找不到,它将回退到运用 Symbol.iterator 的要领。

非线性代码实行

await 一样,for await 轮回会将非线性代码实行引入程序中。也就是说,你的代码将会以和编写的代码差别的次序运转。

当你的程序第一次碰到 for await 轮回时,它将在你的对象上挪用 next

该对象将 yield 一个 promise,然后代码的实行将会脱离你的 async 函数,而且你的程序将继承在该函数以外实行

一旦你的 promise 得到处理,代码实行将会运用这个值返回到轮回体

当轮回完毕并举行下一个路程时,Node.js 将在对象上挪用 next。该挪用会发生另一个 promise,代码实行将会再次脱离你的函数。反复这类形式,直到 Promise 剖析为 donetrue 的对象,然后在 for await 轮回以后继承实行代码。

下面的例子可以申明一点:

let count = 0
const getCount = () => {
  count++
  return `${count}. `
}

const createAsyncGenerator = async function*() {
  console.log(getCount() + 'entering createAsyncGenerator')

  console.log(getCount() + 'about to yield a')
  yield await new Promise((r)=>r('a'))

  console.log(getCount() + 're-entering createAsyncGenerator')
  console.log(getCount() + 'about to yield b')
  yield 'b'

  console.log(getCount() + 're-entering createAsyncGenerator')
  console.log(getCount() + 'about to yield c')
  yield 'c'

  console.log(getCount() + 're-entering createAsyncGenerator')
  console.log(getCount() + 'exiting createAsyncGenerator')
}

const main = async () => {
  console.log(getCount() + 'entering main')

  const asyncGenerator = createAsyncGenerator()
  console.log(getCount() + 'starting for await loop')
  for await(const item of asyncGenerator) {
    console.log(getCount() + 'entering for await loop')
    console.log(getCount() + item)
    console.log(getCount() + 'exiting for await loop')
  }
  console.log(getCount() + 'done with for await loop')
  console.log(getCount() + 'leaving main')
}

console.log(getCount() + 'before calling main')
main()
console.log(getCount() + 'after calling main')

这段代码你用了编号的日记纪录语句,可以让你跟踪其实行情况。作为演习,你须要本身运转程序然后检察实行结果是如何的。

假如你不晓得它的工作体式格局,就会使程序的实行发生杂沓,但异步迭代的确是一项壮大的手艺。

更多编程相干学问,请接见:编程入门!!

以上就是深入研讨Node.js中的异步生成器和异步迭代的细致内容,更多请关注ki4网别的相干文章!

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
深入研究Node.js中的异步生成器和异步迭代_WEB前端开发

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章

本文来源:搜奇网

本文地址:https://www.sou7.cn/300799.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

推荐阅读