经由过程异步迭代简化Node.js流程 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

经由过程异步迭代简化Node.js流程

申博_安全预警 申博 120次浏览 已收录 0个评论

假如我们运用异步迭代,那末运用Node.js流程将越发高效。

异步迭代和异步生成器

异步迭代是用于异步检索数据容器内容的协定,这也意味着当前“使命”能够在检索项目之前被停息。

异步生成器有助于异步迭代,以下所示,就是一个异步生成器函数:

/**
 * @returns an asynchronous iterable
 */
async function* asyncGenerator(asyncIterable) {
  for await (const item of asyncIterable) { // input
    if (···) {
      yield '> ' + item; // output
    }
  }
}

for-await-of轮回遍历输入asyncIterable,这个轮回在一般的异步函数中也可用。别的,yield将值输入到此生成器返回的异步迭代中。

接下里,请亲昵关注以下函数是异步函数照样异步生成器函数:

/** @returns a Promise */
async function asyncFunction() { /*···*/ }

/** @returns an async iterable */
async function* asyncGeneratorFunction() { /*···*/ }

Node.js支撑多种流程,比方:

1.可读流程(Readable stream)是我们能够从中读取数据的流程,换句话说,它们是数据的泉源。比方可读的文件流程,它许可我们读取文件的内容。

2.可写流程(Writable stream)是我们能够写入数据的流程,换句话说,它们是数据的吸收器。比方可写的文件流程,它许可我们将数据写入文件。

3.转换流程(transform stream)既可读又可写,作为可写流程时,它吸收数据片断,对其举行转换,变动或删除它们,然后将它们作为可读流程输出。

流水线手艺(Pipelining)

计算机中的流水线是把一个反复的历程分解为多少个子历程,每一个子历程与其他子历程并行举行。由于这类事情体式格局与工场中的生产流水线十分相似, 因而称为流水线手艺。从本质上讲,流水线手艺是一种时候并行手艺。

要在多个步骤中处置惩罚流程数据,即可运用流水线手艺:

1.经由过程可读的流程吸收输入;

2.每一个处置惩罚步骤都是经由过程转换流程实行的;

3.关于末了一个处置惩罚步骤,我们有两个选项:

3.1我们能够将近来的可读流程中的数据写入可写流程,也就是说,可写流程是流水线上的末了一个历程。

3.2我们能够以其他体式格局处置惩罚近来的可读流程中的数据;

个中,第2个选项是可选的。

文本编码

当建立文本流程时,最好一向指定一个编码:

Node.js文档有一个支撑编码和默许拼写的列表,以下所示:

'utf8'
'utf16le'
'base64'

也能够运用一些差别的拼写,你能够运用Buffer.isEncoding()来搜检哪些是:

> buffer.Buffer.isEncoding('utf8')
true
> buffer.Buffer.isEncoding('utf-8')
true
> buffer.Buffer.isEncoding('UTF-8')
true
> buffer.Buffer.isEncoding('UTF:8')
false

编码的默许值是null,它相当于’utf8’。

辅佐函数:readableToString()  

有时候,我们会运用以下辅佐函数。不过在本文中,你不需要详细相识它是怎样事情的,大抵相识既可。

import * as stream from 'stream';

/**
 * Reads all the text in a readable stream and returns it as a string,
 * via a Promise.
 * @param {stream.Readable} readable
 */
function readableToString(readable) {
  return new Promise((resolve, reject) => {
    let data = '';
    readable.on('data', function (chunk) {
      data += chunk;
    });
    readable.on('end', function () {
      resolve(data);
    });
    readable.on('error', function (err) {
      reject(err);
    });
  });
}

这个函数是经由过程基于事宜的API完成的,稍后我们将看到一种更简朴的要领,即经由过程异步迭代要领。

在这篇文章中,我们将只运用文本流程。

在这些示例中,我们偶然会碰到在顶层运用await的状况。在本文的示例中,我们假定我们在模块内部或异步函数的主体内部。

每当有换行符时,我们都邑运用以下函数:

Unix: '\n' (LF)
Windows: '\r\n' (CR LF)

能够经由过程模块os中的常量EOL接见当前平台的换行符

可读的流程

建立可读的流程

能够从文件中建立可读的流程,详细来说,我们能够运用fs.createReadStream()来建立可读的流程:

import * as fs from 'fs';

const readableStream = fs.createReadStream(
  'tmp/test.txt', {encoding: 'utf8'});

assert.equal(
  await readableToString(readableStream),
  'This is a test!\n');

Readable.from():经由过程可迭代器建立可读流程

静态要领readable .from(iterable, options?)能够建立一个可读的流程,该流程包含可迭代器中包含的数据。可迭代能够是同步迭代的,也能够是异步迭代的。参数选项是可选的,还能够用于指定文本编码。

import * as stream from 'stream';

function* gen() {
  yield 'One line\n';
  yield 'Another line\n';
}
const readableStream = stream.Readable.from(gen(), {encoding: 'utf8'});
assert.equal(
  await readableToString(readableStream),
  'One line\nAnother line\n');

经由过程字符串建立可读的流程

Readable.from() 接收任何可迭代的对象,同时也能够用来将字符串转换成流程:

import {Readable} from 'stream';

const str = 'Some text!';
const readable = Readable.from(str, {encoding: 'utf8'});
assert.equal(
  await readableToString(readable),
  'Some text!');

如许,Readable.from()可将其他可迭代器视为字符串,在其代码点上举行迭代。虽然这在迭代机能方面并不抱负,但在大多数状况下应该是够用了。我愿望Readable.from()要常常与字符串一同运用,所以它未来大概会有被优化。

经由过程for-await-of读取可读流程

每一个可读的流程都是异步可迭代的,这意味着我们能够运用for-await-of轮回来读取它的内容:

import * as fs from 'fs';

async function logChunks(readable) {
  for await (const chunk of readable) {
    console.log(chunk);
  }
}

const readable = fs.createReadStream(
  'tmp/test.txt', {encoding: 'utf8'});
logChunks(readable);

// Output:
// 'This is a test!\n'

在字符串中网络可读流程的内容

下面的函数是我们在这篇文章开头看到的函数的一个更简朴的从新完成历程:

OpenBSD多个严重认证绕过漏洞

OpenBSD是内置安全的开源操作系统。近期Qualys安全研究人员在OpenBSD中发现了多个高危安全漏洞,CVE编号为CVE-2019-19521,CVE-2019-19520,CVE-2019-19522,CVE-2019-19519。这4个漏洞分别是认证绕过和本地权限提升漏洞。 CVE-2019-19521 CVE-2019-19521是一个认证绕过漏洞。该漏洞位于OpenBSD认证框架处理用户通过smtpd, ldapd, radiusd, su, 或sshd服务登陆时提供的用户名的方式中。 远程攻击者利用该漏洞可以成功访问任意有漏洞的服务,只需要在用户名中输入”-schallenge” 或”-schallenge: passwd”,然后输入任意密码,就会让OpenBSD将-翻译为命令行选项,而非用户名。 OpenBSD的认证框

import {Readable} from 'stream';

async function readableToString2(readable) {
  let result = '';
  for await (const chunk of readable) {
    result += chunk;
  }
  return result;
}

const readable = Readable.from('Good morning!', {encoding: 'utf8'});
assert.equal(await readableToString2(readable), 'Good morning!');

注重,在本文的示例中,我们必需运用异步函数,由于我们愿望返回一个Promise。

经由过程异步生成器转换可读流程

异步迭代供应了一个轻松的挑选体式格局,将底本一个团体的流程处置惩罚流程分解成多个步骤:

1.输入历程就是一个可读的流程;

2.第一个转换是由异步生成器实行的,异步生成器在可读流程上迭代,并根据需要生成响应的效果。

3.我们能够挑选运用更多的异步生成器来进一步转换。

4.末了,我们有几个选项来处置惩罚异步迭代返回的末了一个生成器:

4.1我们能够经由过程readable .from()将其转换为可读的流程(稍后能够经由过程流水线手艺将其转换为可写的流程)。

4.2我们能够运用异步函数来处置惩罚它。

鄙人面的示例中,末了一步由async函数logLines()实行,该函数以可迭代的体式格局将项目记录到控制台。

/**
 * @param chunkIterable An asynchronous or synchronous iterable
 * over “chunks” (arbitrary strings)
 * @returns An asynchronous iterable over “lines”
 * (strings with at most one newline that always appears at the end)
 */
async function* chunksToLines(chunkIterable) {
  let previous = '';
  for await (const chunk of chunkIterable) {
    previous += chunk;
    while (true) {
      const eolIndex = previous.indexOf('\n');
      if (eolIndex < 0) break;

      // line includes the EOL
      const line = previous.slice(0, eolIndex+1);
      yield line;
      previous = previous.slice(eolIndex+1);
    }
  }
  if (previous.length > 0) {
    yield previous;
  }
}

async function* numberLines(lineIterable) {
  let lineNumber = 1;
  for await (const line of lineIterable) {
    yield lineNumber + ' ' + line;
    lineNumber++;
  }
}

async function logLines(lineIterable) {
  for await (const line of lineIterable) {
    console.log(line);
  }
}

const chunks = Readable.from(
  'Text with\nmultiple\nlines.\n',
  {encoding: 'utf8'});
logLines(numberLines(chunksToLines(chunks)));

// Output:
// '1 Text with\n'
// '2 multiple\n'
// '3 lines.\n'

可写的流程

为文件建立可写的流程

我们能够运用fs.createWriteStream()来建立可写的流程:

const writableStream = fs.createWriteStream(
  'tmp/log.txt', {encoding: 'utf8'});

写入可写流程

在本节中,我们将引见三种将数据写入可写流程的要领:

1.经由过程.write()要领直接写入可写流程;

2.运用可读流程的.pipe()要领将其导入可写流程;

3.运用函数pipeline ()从模块流程将可读流程导入可写流程;

为了演示这些要领,我们会以三种差别的体式格局完成了同一个函数writeIterableToFile()。

在异步函数中写入可写流程

import * as util from 'util';
import * as stream from 'stream';
import * as fs from 'fs';
import {once} from 'events';

const finished = util.promisify(stream.finished); // (A)

async function writeIterableToFile(iterable, filePath) {
  const writable = fs.createWriteStream(filePath, {encoding: 'utf8'});
  for await (const chunk of iterable) {
    if (!writable.write(chunk)) { // (B)
      // Handle backpressure
      await once(writable, 'drain');
    }
  }
  writable.end(); // (C)
  // Wait until done. Throws if there are errors.
  await finished(writable);
}

await writeIterableToFile(
  ['One', ' line of text.\n'], 'tmp/log.txt');
assert.equal(
  fs.readFileSync('tmp/log.txt', {encoding: 'utf8'}),
  'One line of text.\n');

stream.finished() 的默许版本是基于回调的,然则能够经由过程util.promisify()将其转换为基于商定的版本(A行)。

为此,我们运用了以下两种形式:

1.在处置惩罚backpressure 时写入可写流程(B行):

if (!writable.write(chunk)) {
  await once(writable, 'drain');
}

2.封闭一个可写流程,并守候写入完成(C行):

writable.end();
await finished(writable);

流水线手艺(可读、可写)

import * as stream from 'stream';
import * as fs from 'fs';
const pipeline = util.promisify(stream.pipeline);

async function writeIterableToFile(iterable, filePath) {
  const readable = stream.Readable.from(
    iterable, {encoding: 'utf8'});
  const writable = fs.createWriteStream(filePath);
  await pipeline(readable, writable); // (A)
}
await writeIterableToFile(
  ['One', ' line of text.\n'], 'tmp/log.txt');
// ···

我们运用以下形式(A行):

await pipeline(readable, writable);

另有readable .prototype.pipe(),然则该要领有一个正告(假如可读对象发出毛病,那末可写的就不会自动封闭),但stream.pipeline() 就没有如许的正告。

与流程相干的功用

模块操作系统:

const EOL: string(since 0.7.8)

包含当前平台运用的行尾字符序列。

模块缓冲区:

Buffer.isEncoding(encoding: string): boolean (since 0.9.1)

假如编码正确地为文本指定一个受支撑的Node.js编码,则返回true。支撑的编码包含:

'utf8'
'utf16le'
'ascii'
'latin1
'base64'
'hex'

模块流程:

Readable.prototype[Symbol.asyncIterator](): AsyncIterableIterator<any> (since 10.0.0)

可读流程是异步可迭代的,比方,你能够在asyc函数或异步生成器中运用For -await-of轮回来遍历它们。

finished(stream: ReadableStream | WritableStream | ReadWriteStream, callback: (err?: ErrnoException | null) => void): () => Promise<void> (since 10.0.0)

当读取/写入完成或涌现毛病时,返回的许诺将被处理。

这个许诺的版本是如许建立的:

const finished = util.promisify(stream.finished);
pipeline(...streams: Array<ReadableStream|ReadWriteStream|WritableStream>): Promise<void>(since 10.0.0)

流程之间的流水线手艺,当流水线手艺完成或涌现毛病时,将处理返回的许诺。

这个许诺的版本是如许建立的:

const pipeline = util.promisify(stream.pipeline);
Readable.from(iterable: Iterable<any> | AsyncIterable<any>, options?: ReadableOptions): Readable (since 12.3.0)

将迭代器转换为可读的流程:

interface ReadableOptions {
  highWaterMark?: number;
  encoding?: string;
  objectMode?: boolean;
  read?(this: Readable, size: number): void;
  destroy?(this: Readable, error: Error | null,
    callback: (error: Error | null) => void): void;
  autoDestroy?: boolean;
}

这些选项与可读组织函数的选项雷同,并在此处举行了申明。

模块fs:

createReadStream(path: string | Buffer | URL, options?: string | {encoding?: string; start?: number}): ReadStream (since 2.3.0)

该模块为建立可读的流程供应了更多的挑选:

createWriteStream(path: PathLike, options?: string | {encoding?: string; flags?: string; mode?: number; start?: number}): WriteStream(since 2.3.0)

经由过程选项.flags,你能够指定是不是要写入照样要追加,并对文件在存在或不存在时发作的状况举行疾速处置惩罚。

本文翻译自:https://2ality.com/2019/11/nodejs-streams-async-iteration.html#pipelining


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明经由过程异步迭代简化Node.js流程
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址