有用的内置Node.js APIs( 五 )

part1设为空字符串,part2设为整个块 。如果part2变得非常大--也许超过100,000个字符,因为没有回车符--将part2追加到part1,并将part2设为空字符串 。这将确保被保存的部分不会无限地增长 。

  • 缩小和输出part1
  • 保存part2(它被添加到下一个块的开始) 。
  • 该过程对每个传入的数据块都会再次运行 。
    Worker Threads官方文档是这么说的:Workers(线程)对于执行CPU密集型的JavaScript操作很有用 。它们对I/O密集型的工作帮助不大 。Node.js内置的异步I/O操作比Workers的效率更高 。
    假设一个用户可以在你的Express应用程序中触发一个复杂的、十秒钟的JavaScript计算 。该计算将成为一个瓶颈,使所有用户的处理程序停止 。你的应用程序不能处理任何请求或运行其他功能,除非它计算完成 。
    异步计算处理来自文件或数据库数据的复杂计算可能问题不大,因为每个阶段在等待数据到达时都是异步运行 。数据处理发生在事件循环的不同迭代中 。
    然而,仅用JavaScript编写的长运行计算,比如图像处理或机器学习算法 , 将占用事件循环的当前迭代 。
    一种解决方案就是worker线程 。这类似于浏览器的web worker以及在独立线程上启动JavaScript进程 。主线程和worker线程可以交换信息来触发或者终止程序 。
    Workers和事件循环Workers对CPU密集型JavaScript操作很有用 , 尽管Node.js的主事件循环仍应用于异步I/O活动 。
    示例代码有一个worker项目,其在lib/dice.js中导出diceRun()函数 。这是将任意数量的N面骰子投掷若干次,并记录总分的计数(应该是正态分布曲线的结果):
    // dice throwingexport function diceRun(runs = 1, dice = 2, sides = 6) {const stat = [];while (runs > 0) {let sum = 0;for (let d = dice; d > 0; d--) {sum += Math.floor( Math.random() * sides ) + 1;}stat[sum] = (stat[sum] || 0) + 1;runs--;}return stat;}index.js中的代码启动一个进程,每秒钟运行一次并输出一条信息:
    // run process every secondconst timer = setInterval(() => {console.log('another process');}, 1000);调用diceRun()函数,将两个骰子抛出10亿次:
    import { diceRun } from './lib/dice.js';// throw 2 dice 1 billion timesconstnumberOfDice = 2,runs = 999_999_999;const stat1 = diceRun(runs, numberOfDice);这将暂停计时器 , 因为Node.js事件循环在计算完成之前不能继续下一次迭代 。
    然后,将上述代码在一个新的Worker中尝试相同的计算 。这会加载一个名为worker.js的脚本,并在配置对象上的workerData属性传递计算参数:
    import { Worker } from 'worker_threads';const worker = new Worker('./worker.js', { workerData: { runs, numberOfDice } });事件处理器被附加到运行worker.js脚本的worker对象上 , 以便它能接收传入的结果:
    // result returnedworker.on('message', result => {console.table(result);});以及处理错误:
    // worker errorworker.on('error', e => {console.log(e);});以及在处理完成后进行整理:
    // worker completeworker.on('exit', code => {// tidy up});worker.js脚本启动diceRun()计算,并在计算完成后向父脚本发布一条消息--该消息由上面的message处理器接收:
    // worker threadimport { workerData, parentPort } from 'worker_threads';import { diceRun } from './lib/dice.js';// worker threadimport { workerData, parentPort } from 'worker_threads';import { diceRun } from './lib/dice.js';// start calculationconst stat = diceRun( workerData.runs, workerData.numberOfDice );// post message to parent scriptparentPort.postMessage( stat );worker运行时,计时器并没有暂停 , 因为它是在另一个CPU线程上执行的 。换句话说,Node.js的事件循环继续迭代,而没有长延迟 。
    使用node index.js运行项目代码 。
    有用的内置Node.js APIs

    文章插图
    你应该注意到了 , 基于worker的计算运行速度稍快,因为线程完全专用于该进程 。如果你的应用程序中遇到性能瓶颈,请考虑使用worker
    Child Processes有时需要调用那些不是用Node.js编写的或者有失败风险的应用程序 。
    真实案例我写过一个Express应用程序,该程序生成了一个模糊的图像哈希值 , 用于识别类似的图形 。它以异步方式运行,并且运行良好,直到有人上传了一个包含循环引用的畸形GIF(动画帧A引用了帧B,而帧B引用了帧A) 。

    推荐阅读