【问题标题】:Node.js writableStream still emit events after end() callNode.js writableStream 在 end() 调用后仍然发出事件
【发布时间】:2022-08-18 16:59:31
【问题描述】:

抱歉这个可能是微不足道的问题,但我仍然无法了解流在 node.js 中的工作方式。

我想解析一个 html 文件并获取我遇到的第一个脚本的路径。我想在第一次匹配后中断解析,但仍然调用 onopentag() 侦听器,直到 html 文件有效结束。为什么 ?

  const { WritableStream } = require(\"htmlparser2/lib/WritableStream\"); 
  const scriptPath = await new Promise(function(resolve, reject) {
    try {
      const parser = new WritableStream({
        onopentag: (name, attrib) => {
          if (name === \"script\" && attrib.src) {
            console.log(`script : ${attrib.src}`);
            resolve(attrib.src);  // return the first script, effectively called for each script tag
            // none of below calls seem to work
            indexStream.unpipe(parser);
            parser.emit(\"close\");
            parser.end();
            parser.destroy();                
          }
        },
        onend() {
          resolve();
        }
      });
      const indexStream = got.stream(\"/index.html\", {
        responseType: \'text\',
        resolveBodyOnly: true
      });
      indexStream.pipe(parser); // and parse it
    } catch (e) {
      reject(e);
    }
  });

是否可以在 indexStream 有效结束之前关闭解析器流,如果可以,如何? 如果不是为什么?

请注意,代码有效,并且我的承诺使用第一个匹配有效地解决了。

    标签: javascript node.js node-streams


    【解决方案1】:

    关于 WriteableStream 的工作原理有些混乱。首先,当你这样做时:

    const parser = new WritableStream(...)
    

    这是误导。真的应该是这样的:

    const writeStream = new WritableStream(...)
    

    实际的 HTML 解析器是 WritableStream 对象中名为 ._parser 的实例变量(参见 code)。而且,正是那个解析器发出了onopentag() 回调,并且因为它正在处理一个可能有一些累积的文本与读取流断开连接的缓冲区,所以它可能不会立即停止仍然来自缓冲数据的事件。

    解析器本身有一个公共的reset()method,如果与读取流断开连接,然后您调用了该重置方法,它应该停止发出事件。

    你可以试试这个(我不是 TypeScript 人,所以你可能需要修改一些东西才能让 TypeScript 编译器满意,但希望你能在这里看到这个概念):

      const { WritableStream } = require("htmlparser2/lib/WritableStream"); 
      const scriptPath = await new Promise(function(resolve, reject) {
        try {
          const writeStream = new WritableStream({
            onopentag: (name, attrib) => {
              if (name === "script" && attrib.src) {
                console.log(`script : ${attrib.src}`);
                resolve(attrib.src);  // return the first script, effectively called for each script tag
                // disconnect the readstream
                indexStream.unpipe(writeStream);
                // reset the internal parser so it clears any buffers it
                // may still be processing
                writeStream._parser.reset();
              }
            },
            onend() {
              resolve();
            }
          });
          const indexStream = got.stream("/index.html", {
            responseType: 'text',
            resolveBodyOnly: true
          });
          indexStream.pipe(writeStream); // and parse it
        } catch (e) {
          reject(e);
        }
      });
    

    【讨论】:

    • 好的,它有效。所以这只是一个实现和缓冲的问题。但是,我希望 end() 或 destroy() 方法能够有效地关闭流,包括清除缓冲区,但是……谢谢!
    猜你喜欢
    • 2018-01-25
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多