ホームページ  >  記事  >  ウェブフロントエンド  >  ノードを使用して読みやすいフロー パターンを作成する方法

ノードを使用して読みやすいフロー パターンを作成する方法

php中世界最好的语言
php中世界最好的语言オリジナル
2018-06-04 09:51:141101ブラウズ

今回は、nodeを使って読みやすいフローパターンを作成する方法と、nodeを使って読みやすいフローパターンを作成する際の注意点を紹介します。実際の事例を見てみましょう。

ノードの読み取り可能なストリームは、読み取り可能なストリームのイベント

フローモードに基づいています。このフローモードは、「スイッチ」がオンになるたびに有効になります。 " 「スイッチ」が一時停止に設定されている場合、この読み取り可能なストリームは、「スイッチ」がフローにリセットされるまで ファイルを読み取りません

ファイルの読み取りプロセス

ファイルの内容を読み取るプロセスは主に次のとおりです。

  1. ファイルが正常に開かれると、open イベントがトリガーされ、開くことが失敗すると、error イベントがトリガーされます。そして close イベントがトリガーされ、ファイルが閉じられます。

  2. ファイルの内容の読み取りを開始し、データ イベントをリッスンします。データはフロー状態にあり、スイッチのステータスを変更することで読み取りを一時停止できます。

  3. コンテンツが読み取られるたびに、コンテンツはキャッシュに入れられ、データはデータイベントを通じて公開されます。

  4. ファイルの内容を読んだ後、ファイルを閉じます。

この一連のアクションはすべてイベントに基づいており、ノード内のイベントがパブリッシュ/サブスクライブ モデルで実装されていることは誰もが知っています。

ノードが読み取り可能なストリームを使用してファイルの内容を読み取る方法を見てみましょう?

ノードの読み取り可能なストリームのパラメーター

まず、fs モジュールを通じて読み取り可能なストリームを作成します。

  1. 最初のパラメーターは、読み取るファイルのアドレスをここで指定します。読みたい。

  2. 2 番目のパラメーターはオプションで、読み取り可能なストリームの特定のパラメーターを指定するために使用されるオブジェクトです。

次のパラメータを 1 つずつ説明します。

  • highWaterMark: このパラメータは主に、ファイルの内容をキャッシュに読み取るときに使用されます。ここでは、このデータをキャッシュするためのバッファを作成する必要があるため、このパラメータを使用してバッファのサイズを設定します。このパラメータが設定されていない場合、デフォルトの読み取り可能なストリーム構成は 64k です。

  • flags: このパラメータは主にファイルの実行モードを設定するために使用されます。たとえば、特定の操作はファイルの読み取りまたは書き込みに適しています。ファイルに書き込む場合は、w を使用します。ファイルを読み取る場合、この演算子は r である必要があります。

次の表は、異なる記号が異なる意味を表すことを示しています:

記号意味rファイルを読み取ると、ファイルは存在せず、エラーが報告されますr+ 読み取りと書き込み。ファイルが存在しない場合はエラーが報告されますrsファイルを同期的に読み取り、キャッシュを無視しますwファイルを書き込み、存在する場合は作成します存在しません、存在する場合はクリアしますwx ファイルを排他的に書き込みますw+ ファイルの読み取りと書き込み、存在しない場合は作成し、存在する場合はクリアしますwx+ w+と同様に、排他的に開きます a書き込みを追加します ax aと同様に、排他的な方法で書き込みます a+読み取りと書き込みの場合は追加します存在しないため、ax+を作成します。a+のように動作しますが、排他的な方法でファイルを開きます
  • autoClose: このパラメータは主にファイルのクローズを制御するために使用されます。ファイルを再度開く処理中またはその他の操作中にエラーが発生した場合は、ファイルを閉じる必要があります。このパラメータは、ファイルを自動的に閉じるかどうかを設定する関数です。

  • エンコーディング: ノードはバッファを使用してファイル操作のバイナリデータを読み取ります。これらのデータが表示されると、意味不明なデータが大量に表示されるため、このデータに対して特定のエンコード形式を指定する必要があります。その後、データはエンコードされ、変換されたデータが私たちが理解できるデータになります。

  • starts: このパラメータは主に、ファイルの内容の読み取りを開始する位置を指定するために使用されます。デフォルトは 0 から始まります。

  • ends: このパラメータは主に、ファイルのデータを読み取る時間を指定するために使用されます。このパラメータには、いわゆるパッケージの前後という独自の位置が含まれていることをここで説明する必要があります。 。

読み取り可能なストリームの具体的な例を見てみましょう:

let fs = require("fs");
let rs = fs.createReadStream("./a.js", {
  highWaterMark: 3,
  encoding: "utf8",
  autoClose: true,
  start: 0,
  end: 9
});
rs.on("open", () => {console.log("open");});
rs.on("close", () => {console.log("close");});
rs.on("data", data => {
  console.log(data);
  rs.pause();//暂停读取 此时流动模式为暂停模式
});
setInterval(() => {
  rs.resume();//重新设置为流动模式,开始读取数据
}, 1000);
rs.on("end", () => { console.log("end"); });
rs.on("error", err => { console.log(err); });

手書きの読み取り可能なストリームの最初のステップ

上で述べたように、ノードの読み取り可能なストリームはノードのコアモジュールイベントに基づいて完了します。独自の読み取り可能なストリームを実装するには、イベント モジュールを継承する必要があります。コードは次のとおりです。

let fs = require('fs');
let EventEmitter = require('events');
class ReadStream extends EventEmitter {
}

EventEmitter クラスを継承すると、EventEmitter クラスのさまざまなメソッドを使用でき、イベントを処理するためにパブリッシュおよびサブスクライブ モデルも使用できます。

ステップ 2: 読み取り可能なストリーム設定のパラメーターを処理する

ノードで読み取り可能なストリームを作成するときに、このストリームの特定のパラメーター (

let rs = fs.createReadStream("./a.js", {
  highWaterMark: 3,
  encoding: "utf8",
  autoClose: true,
  start: 0,
  end: 9
});

など) を設定できることを上で説明しました。したがって、これらのパラメーターについては、自分で実装したリーダブルストリームクラスもこれらのパラメータを処理する必要があるのですが、これらのパラメータはどのように処理すればよいのでしょうか?

constructor(path, options = {}) {
  super();
  this.path = path; //指定要读取的文件地址
  this.highWaterMark = options.highWaterMark || 64 * 1024;
  this.autoClose = options.autoClose || true; //是否自动关闭文件
  this.start = options.start || 0; //从文件哪个位置开始读取
  this.pos = this.start; // pos会随着读取的位置改变
  this.end = options.end || null; // null表示没传递
  this.encoding = options.encoding || null;// buffer编码
  this.flags = options.flags || 'r';
  this.flowing = null; // 模式开关
  this.buffer = Buffer.alloc(this.highWaterMark);// 根据设置创建一个buffer存储读出来的数
  this.open();
}

通常の設定原則は、ユーザーが設定したパラメーターに基づきます。ユーザーがこのパラメーターを設定しない場合は、デフォルトの設定が使用されます。

読み取り可能なストリームを実装するための 3 番目のステップ: ファイルを開きます

ここでの原則は、ノード モジュール fs で open メソッドを使用することです。まず、fs.open() メソッドの使用法を確認してみましょう。

fs.open(filename,flags,[mode],callback);
//实例
fs.open('./1,txt','r',function(err,fd){});

ここで説明する必要があります。コールバック関数callbackには2つのパラメータがあります:

  1. 最初のパラメータはエラーで、特定のエラーメッセージを説明するためにノードの非同期コールバックによって返されるパラメータです

  2. 2 番目のパラメータは fd で、これはファイルを識別するために使用されるファイル記述子であり、open 関数の最初のパラメータに相当します

さて、それでは独自の open メソッドを見てみましょう読み取り可能なストリーム 実装方法:

open() {
  fs.open(this.path, this.flags, (err, fd) => { 
    //fd标识的就是当前this.path这个文件,从3开始(number类型)
    if (err) {
      if (this.autoClose) { // 如果需要自动关闭则去关闭文件
        this.destroy(); // 销毁(关闭文件,触发关闭事件)
      }
      this.emit('error', err); // 如果有错误触发error事件
      return;
    }
    this.fd = fd; // 保存文件描述符
    this.emit('open', this.fd); // 触发文件的打开的方法
  });
}

コードからわかります:

fs.open 関数は非同期関数であり、ファイルが正常に開かれると、コールバックが非同期で実行されることを意味します。はい、これには注意が必要です。

もう 1 つの重要な点は、ファイルを開くときにエラーが発生した場合、それはファイルを開くことが失敗したことを意味し、この時点でファイルを閉じる必要があるということです。

読み取り可能なストリームを実装するステップ 4: ファイルのコンテンツを読み取る

上で詳しく述べたように、ファイル内のコンテンツを読み取りたい場合、読み取り可能なストリーム自体が「スイッチ」を定義します。スイッチ」をオンにする必要があるのですが、ノード読み取り可能なストリーム自体はどのようにしてこの「スイッチ」をオンにするのでしょうか?

データイベントをリッスンする

ノード読み取り可能なストリームは、データイベントをリッスンすることでこの「スイッチ」のオープンを実現します:

rs.on("data", data => {
  console.log(data);
});

ユーザーがデータイベントをリッスンすると、「スイッチ」がオンになり、ファイルからコンテンツを継続的に読み取ります。では、ノードはどのようにしてデータ イベントをリッスンするのでしょうか?
答えは、イベント モジュールの newListener です

これは、ノードで読み取り可能なストリームがイベントに基づいており、イベントでは、サーバーが newListener イベントを通じてユーザーからのすべてのイベントをリッスンでき、各イベントに対応するtype を使用すると、ユーザーがデータ イベントをリッスンしているときに、それを取得してファイル内のコンテンツを読み取ることができます。では、独自の読み取り可能なストリームを実装するにはどうすればよいでしょうか。

// 监听newListener事件,看是否监听了data事件,如果监听了data事件的话,就开始启动流动模式,读取文件中的内容
this.on("newListener", type => {
  if (type === "data") {
    // 开启流动模式,开始读取文件中的内容
    this.flowing = true;
    this.read();
  }
});

好了,知道了这个"开关"是如何打开的,那么这个时候就到了真正读取文件中内容的关键时候了,先上代码先:

read() {
  // 第一次读取文件的话,有可能文件是还没有打开的,此时this.fd可能还没有值
  if (typeof this.fd !== "number") {
    // 如果此时文件还是没有打开的话,就触发一次open事件,这样文件就真的打开了,然后再读取
    return this.once("open", () => this.read());
  }
  // 具体每次读取多少个字符,需要进行计算,因为最后一次读取倒的可能比highWaterMark小
  let howMuchRead = this.end ? Math.min(this.end - this.pos + 1, this.highWaterMark) : this.highWaterMark;
  fs.read(this.fd, this.buffer, 0, howMuchRead, this.pos, (err, byteRead) => {
    // this.pos 是每次读取文件读取的位置,是一个偏移量,每次读取会发生变化
    this.pos += byteRead;
    // 将读取到的内容转换成字符串串,然后通过data事件,将内容发布出去
    let srr = this.encoding ? this.buffer.slice(0, byteRead).toString(this.encoding) : this.buffer.slice(0, byteRead);
    // 将内容通过data事件发布出去
    this.emit("data", srr);
    // 当读取到到内容长度和设置的highWaterMark一致的话,并且还是流动模式的话,就继续读取
    if ((byteRead === this.highWaterMark) && this.flowing) {
      return this.read();
    }
    // 没有更多的内容了,此时表示文件中的内容已经读取完毕
    if (byteRead < this.highWaterMark) {
      // 读取完成,发布end方法,并关闭文件
      this.emit("end");
      this.destory();
    }
  });
}

这里我们特别要注意的是:

  1. 文件是否已经打开,是否获取到fd,如果没有打开的话,则再次触发open方法

  2. 分批次读取文件内容,每次读取的内容是变化的,所以位置和偏移量是要动态计算的

  3. 控制读取停止的条件。

实现可读流第五步:关闭文件

好了,到现在,基础的读取工作已经完成,那么就需要将文件关闭了,上面的open和read方法里面都调用了一个方法:destory,没错,这个就是关闭文件的方法,好了,那么我们来看看这个方法该如何实现吧

destory() {
  if (typeof this.fd !== "number") {
    // 发布close事件
    return this.emit("close");
  }
  // 将文件关闭,发布close事件
  fs.close(this.fd, () => {
    this.emit("close");
  });
}

当然这块的原理就是调用fs模块的close方法啦。

实现可读流第六步:暂停和恢复

既然都说了,node可读流有一个神奇的"开关",就像大坝的阀门一样,可以控制水的流动,同样也可以控制水的暂停啦。当然在node可读流中的暂停是停止对文件的读取,恢复就是将开关打开,继续读取文件内容,那么这两个分别对应的方法就是pause()和resume()方法。

那么我们自己的可读流类里面该如何实现这两个方法的功能呢?非常简单:

我们在定义类的私有属性的时候,定义了这样一个属性flowing,当它的值为true时表示开关打开,反之关闭。

pause() {
  this.flowing = false;// 将流动模式设置成暂停模式,不会读取文件
}
resume() {
  this.flowing = true;//将模式设置成流动模式,可以读取文件
  this.read();// 重新开始读取文件
}

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

怎样利用JS做出引用传递与值传递

使用JS实做出加密解密操作

以上がノードを使用して読みやすいフロー パターンを作成する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。