Logo Vincent
返回文章列表

一篇文章了解JS并行任务

Web
一篇文章了解JS并行任务

【前言】

在开发过程中大部分场景是顺序执行代码,

也有场景要求并行执行多个任务,

本文研究下如何高效的并行执行任务。

【并行场景】

常见并行执行任务的场景

1.并行执行多个请求

2.并行下载文件

【并行任务】

上述场景中任务的共同点是

1.多个任务没有依赖关系

2.多个任务完成耗时不确定

3.要求最快完成所有任务

所以可以用setTimeout模拟并行任务,

timeout可以传入,模拟不同时长完成任务,

/**
 * handler
 * @param {*} timeout
 * @returns
 */
module.exports = function (timeout) {
  return new Promise(function (resolve) {
    setTimeout(() => {
      return resolve(timeout);
    }, timeout);
  });
};

【并行任务池】

这里的任务池,就是一些timeout的数值集合,

/**
 * values
 */
module.exports = [100, 300, 200, 400];

【并行回调和完成】

并行任务执行,一般希望

1.每个任务完成有回调callback

2.所有任务完成有回调complete

callback代码如下

// q
const q = require("qiao-console");

/**
 * callback
 * @param {*} index
 * @param {*} res
 */
module.exports = function (index, res) {
  q.writeLine(index, `${index} ${res}`);
};

complete代码如下

// q
const q = require("qiao-console");

/**
 * complete
 * @param {*} l
 */
module.exports = function (l) {
  q.writeLine(l, `complete`);
};

【串行执行】

串行执行比较简单,for+await就可以实现

// q
const q = require("qiao-console");

// vars
const values = require("./_values.js");
const handler = require("./_handler.js");
const callback = require("./_callback.js");
const complete = require("./_complete.js");

// normal
async function normal() {
  console.time("qiao-parallel");

  for (let i = 0; i < values.length; i++) {
    const value = values[i];

    const res = await handler(value);
    callback(i, res);
  }
  complete(values.length);

  console.timeEnd("qiao-parallel");
}

// test
(function () {
  q.clear();

  normal();
})();

其中

values:并行任务池数组

handler:并行任务

callback:单个任务执行完毕回调

complete:所有任务执行完毕回调

运行效果如下,总耗时1s(100ms+200ms+300ms+400ms)

【并行执行-IIFE】

上述串行执行的核心代码是

for (let i = 0; i < values.length; i++) {
  const value = values[i];

  const res = await handler(value);
  callback(i, res);
}

这里只需要使用立即执行函数,

即可让几个任务并行执行,

立即执行函数的介绍可以看这里: https://developer.mozilla.org/zh-CN/docs/Glossary/IIFE

修改后的核心代码如下

console.time("qiao-parallel");

for (let i = 0; i < values.length; i++) {
  (async function (i, value) {
    const res = await handler(value);
    callback(i, res);
    if (i + 1 == values.length) {
      complete(values.length);
      console.timeEnd("qiao-parallel");
    }
  })(i, values[i]);
}

运行效果如下,总耗时400ms(max(100,200,300,400))

【并行执行-多进程】

在nodejs下,还可以使用多进程并行执行任务,

多进程执行任务要使用nodejs的child_process模块,

使用方法:

1.准备执行任务js文件

2.fork执行

3.通过process通信

child_process的fork使用,详见:

https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#child_processforkmodulepath-args-options

fork部分的代码大致如下

const cp = child_process.fork(jsPath, args || []);
cp.on("message", function (msg) {
  if (onMsg) onMsg(msg);
});
cp.on("exit", function (code) {
  if (onExit) onExit(code);
});

之前的handler.js文件对应的要修改为fork的模式

// handler
const handler = require("./_handler.js");

// fork handler
async function forkHandler() {
  // check
  if (!process || !process.argv) return;

  // value
  const value = parseInt(process.argv[2]);

  // msg
  const msg = await handler(value);
  process.send(msg);
}

forkHandler();

运行效果如下,总耗时447ms(max(100,200,300,400)+47ms)

运行时间小于串行的1s,大于IIFE的400ms,

其中47ms可以理解为进程创建和销毁的耗时,

将上述100ms,200ms,300ms,400ms修改为1-4s,查看效果,

IIFE执行效果如下, 总耗时还是4s(max(1,2,3,4))

fork执行效果如下,总耗时是4.045s(max(1,2,3,4)+45ms)

以上证明多进程并行执行任务中,

创建和销毁进程大概耗时是40-50ms

【并行执行-多线程】

nodejs从10.5.0开始引入了Worker threads模块,

详见:https://nodejs.org/dist/latest-v18.x/docs/api/worker_threads.html#worker-threads

worker代码如下:

const worker = new Worker(jsPath, {
  workerData: value,
});
worker.on("message", function (res) {
  onCallback(callback, index, res);
});
worker.on("error", function (error) {
  console.log(error);
});
worker.on("exit", function () {
  onComplete(complete, valuesLength);
});

对应的handler代码修改为:

// worker
const { parentPort, workerData } = require("node:worker_threads");

// handler
const handler = require("./_handler.js");

// fork handler
async function forkHandler() {
  // msg
  var msg = await handler(workerData);
  parentPort.postMessage(msg);
}

forkHandler();

运行效果如下,总耗时447ms(max(100,200,300,400)+34ms)

修改为1-4后效果如下,总耗时是4.032s(max(1,2,3,4)+32ms)

可以看到多线程模式额外的开销是30ms左右,

比多进程模式的40-50ms要快一些。

【qiao-parallel】

封装了一个npm包,欢迎使用: https://code.insistime.com/#/qiao-parallel

|— 封装了IIFE并行执行任务

// q
const q = require("qiao-console");

// vars
const values = require("./_values.js");
const handler = require("./_handler.js");
const callback = require("./_callback.js");
const complete = require("./_complete.js");

// parallel
const parallel = require("qiao-parallel");

// test
(function () {
  q.clear();

  parallel.parallelByIIFE(handler, values, callback, complete);
})();

|— 封装了多进程并行执行任务

// q
const q = require("qiao-console");

// vars
const values = require("./_values.js");
const callback = require("./_callback.js");
const complete = require("./_complete.js");

// parallel
const parallel = require("qiao-parallel");

// test
(function () {
  q.clear();

  const jsPath = require("path").resolve(__dirname, "./fork-handler.js");
  parallel.parallelByFork(jsPath, values, callback, complete);
})();

|—封装了多线程并行执行任务

// q
const q = require("qiao-console");

// vars
const values = require("./_values.js");
const callback = require("./_callback.js");
const complete = require("./_complete.js");

// parallel
const parallel = require("qiao-parallel");

// test
(function () {
  q.clear();

  const jsPath = require("path").resolve(__dirname, "./worker-handler.js");
  parallel.parallelByWorker(jsPath, values, callback, complete);
})();

【总结】

1.并行任务-场景介绍

2.并行任务-任务特点

3.并行任务-任务池

4.并行任务-回调

5.并行任务-IIFE模式

6.并行任务-多进程模式

7.并行任务-多线程模式

8.并行任务-https://code.insistime.com/#/qiao-parallel

相关推荐

小巧的JS测试框架:AVA

【前言】 常见的JS测试框架有Jest,Mocha等, 今天介绍一个小巧的JS测试框架:ava, https://github.com/avajs/ava

规范的代码提交:Conventional Commits

【前言】 本文介绍如何提交规范的commit msg, 规范的commit msg可以看这个网站: https://www.conventionalcommits.org/en/v1.0.0/ 【commitizen】 可以手动提交符合规范的commit msg,如下 手动提交符合规范的msg比较麻

一篇文章开发todolist

【前言】 本文实战开发一个todolist, 基于以下技术栈: react:开发ui, https://reactjs.org/ webpack,构建前端项目, https://webpack.js.org/ localStorage,存储数据, https://developer.mozilla.

一篇文章学会IndexedDB

【简介】 IndexedDB,web端可以直接使用的数据库,详见: https://developer.mozilla.org/zhCN/docs/Web/API/IndexedDB\API 【IndexedDB vs LocalStorage】 之前介绍了LocalStorage:一篇文章学会lo

初始化前端Monorepos项目

【前言】 本文记录初始化一个前端Monorepos项目的过程 【LICENSE】 如果是开源项目, 需要添加LICENSE, 一般推荐使用MIT LICENSE, 模板如下, 其中copyright那一行, 可以替换为自己的信息。 【git】 git的一些基础设置 设置git账号信息 配置gitig

一篇文章判断用户是否在线

【前言】 如何判断用户在线,而不是用户网络是否连通, 在线:连网,可以访问internet 连通:连网,不能访问internet 【是否在线】 如上,判断是否在线,需要检测是否可以访问internet nodejs 在nodejs下,可以通过ping一些常见的域名来确定, 这里封装了一个工具, ht

一篇文章学会LocalStorage

【简介】 LocalStorage,即本地存储,详见: https://developer.mozilla.org/zhCN/docs/Web/API/Window/localStorage 【cookie vs LocalStorage】 服务端是否可以获取? cookie:服务端可以获取,每次请

mantine-ui快速上手

【前言】 2023年,前端开发的ui框架应该用哪个呢, 一些基本的诉求: 1.支持pc和移动端 2.支持黑夜&白天主题切换 3.组件丰富 4.支持react 4是技术栈选择,非通用 按以上要求在github上快速搜索下react相关ui库, 从start数,issues数,最后commit时间等维度

前端Monorepos项目使用npm-workspaces

【前言】 之前一直使用 lerna 来管理前端 monorepos 项目, 今天升级 lerna 后发现不支持 bootstrap 命令了, 替换为了 npm 的 workspaces 相关命令。 【lerna bootstrap】 lerna 的相关使用可以看这篇文章, 一文学会用Lerna管理多

Nx-VS-Lerna

【前言】 nx和lerna都是优秀的monorepos工具, 本文来对比一下两者的不同, https://nx.dev/ https://lerna.js.org/ 对比之前可以先看下面两篇文章, 了解nx和lerna的基本使用, 一文学会用Lerna管理多个npm包 强大的构建系统:NX 【任务执

强大的构建系统:NX

【前言】 nx是一个强大的构建系统, 这么说可能比较模糊, 本文实践一个项目,带大家了解nx, 感兴趣的也可以自己探索: https://nx.dev/ nx一些典型的应用场景: 1.基于package的monorepo管理 2.完整的前端项目管理 3.react,angular等项目管理 4.no

一篇文章学会Webpack5.x

【前言】 Web前端构建离不开webpack, 众所周知webpack的配置很多很复杂, 甚至可以设置“webpack配置工程师”, 本文抓住webpack核心概念, 和业务开发中常用配置进行讲解, 争取一篇文章学会webpack使用和配置。 【常用文档】 webpack官网 webpack手册:可

© 2026 vincentqiao.com . 保留所有权利。