首發地址 GitHub服務器
最近 CLI 項目中有一個用戶體驗不是很完美點的,由於須要執行 cli 命令來發送一個請求到服務器,雖然請求發送成功了,可是服務器須要作一些規則操做,這些規則操做的時間,沒有辦法捕獲到而且響應給 cli 中,思來想去,採起了一個障眼法的操做,哈哈哈markdown
successasync
failed函數
首先來看進度條的代碼 相對來講比較簡單,主要的做用就是計算輸出 完成的字符和未完成的字符拼接返回的整字串,以及完成的百分比oop
// TerminalProgress.js
class TerminalProgress {
constructor(description) {
// the description info 進度條前面的描述
this.description = description || "OnProgress";
// the render str length 進度條的長度(這玩意最好別太長,超過控制檯長度輸出就略顯有點bug)
this.length = 50;
}
/** * @Parmas opts {Object} * @Parmas opts.completed {Number} * @Parmas opts.total {Number} * * @return Result {Object} * @return Result.description {String} * @return Result.complete {String} * @return Result.uncomplete {String} * @return Result.progressNum {Number} */
renderOption(opts) {
// Calculate progress (completed / total)
let percent = (opts.completed / opts.total).toFixed(4);
// the complete str length
let cell_num = Math.floor(percent * this.length);
// complete str
let complete = "";
for (let i = 0; i < cell_num; i++) {
complete += "█";
}
// uncompleted str
let uncomplete = "";
for (let i = 0; i < this.length - cell_num; i++) {
uncomplete += "░";
}
//
let progressNum = `${(100 * percent).toFixed(2)}%`;
return { description: this.description, complete, uncomplete, progressNum };
}
}
module.exports = TerminalProgress;
複製代碼
有了上面的進度串,就要思考這個東西應該運行在哪裏
一、直接在主進程中運行先加載完進度串再發送請求-這個作法簡直了 enenene, 發送請求失敗了怎麼辦?,提示用戶執行成功了,可是後面又失敗,心口不一啪啪打臉 ✖ 😂😂😂
post
二、運行在子進程中,主進程加載子進程運行進度串,而後主進程在去發送請求,請求發送成功,主進程經過進程通訊告訴子進程,這個你就順利執行完成就 OK,若是發送失敗,在告訴子進程這個不 OK,見紅。
ui
三、執行 main 主進程 加載子進程輸出進度串,而且主進程設置定時器,在必定的時間之後 發起請求並判斷 響應是否 OK 若是 OK,在此向子進程 send 數據。this
// child_process.js
let chalk = require("chalk"); // 顏色模塊
let slog = require("single-line-log").stdout; // 輸出字符在一行
const TerminalProgress = require("./TerminalProgress"); // 導入進度串 並實例化
const terminalProgress = new TerminalProgress("On Publishing");
var Timer = null;
// 子進程監聽 message 事件
process.on("message", (value) => {
render(value);
});
function render(value) {
let num = value.num;
let total = value.total;
let status = value.status;
let time = value.time;
if (Timer) {
clearInterval(Timer);
}
Timer = setInterval(() => {
if (num <= total) {
let {
description,
complete,
uncomplete,
progressNum,
} = terminalProgress.renderOption({ completed: num, total: total });
if (status) {
let renderString = `${description}: ${chalk.green( complete )}${uncomplete} ${progressNum} \n\n`;
slog(renderString);
} else {
let renderString = `${description}: ${complete}${uncomplete} ${progressNum} \n\n`;
slog(chalk.red(renderString));
}
num++;
} else {
clearInterval(Timer);
// 退出當前進程
process.exit(status);
}
}, time);
}
複製代碼
主進程代碼spa
// main.js
const path = require("path");
const chalk = require("chalk");
const child_process = require("child_process");
let leadTime = 100 // 給定一個初始時間,
let thirtyPercentTime = leadTime * 0.3;
let countDownTime = 100;
let pathString = path.resolve(__dirname, "./child_process.js");
// fork 一個子進程並加載運行
const subprocess = child_process.fork(pathString);
// 向子進程發送初始化數據
subprocess.send({ num: 0, total: leadTime, status: true, time: countDownTime });
// 監聽子進程的運行狀態
subprocess.on("close", async (value) => {
if (value) {
console.log(chalk.green(`發佈成功`));
} else {
console.log(chalk.red("發佈失敗"));
}
process.exit(0);
});
let setFunction = async (count) => {
let isPublishOK = await asyncRequest();
if (isPublishOK) {
subprocess.send({
num: parseInt(leadTime * (count / 10)),
total: leadTime,
status: true,
time: 100,
});
} else {
if (count == 6) {
subprocess.send({
num: parseInt(leadTime * (count / 10)),
total: leadTime,
status: false,
time: 50,
});
} else {
setTimeout(setFunction, thirtyPercentTime * countDownTime, 6);
}
}
};
// 這裏用到了setTimeout的第三個參數 ,他會傳遞給第一個函數
setTimeout(setFunction, thirtyPercentTime * countDownTime, 3);
async function asyncRequest() {
// do something
return true;
}
複製代碼
除過使用子進程還可使用 woker 線程,來加載運行進度串線程
// Woker.js
let slog = require("single-line-log").stdout;
let chalk = require("chalk");
// 使用了 這個庫
const { parentPort, workerData } = require("worker_threads");
const TerminalProgress = require("./TerminalProgress");
const terminalProgress = new TerminalProgress("On Publishing", workerData - 30);
var Timer = null;
parentPort.on("message", (value) => {
render(value);
});
function render(value) {
let num = value.num;
let total = value.total;
let status = value.status;
let time = value.time;
if (Timer) {
clearInterval(Timer);
}
Timer = setInterval(() => {
if (num <= total) {
let {
description,
complete,
uncomplete,
progressNum,
} = terminalProgress.renderOption({ completed: num, total: total });
if (status) {
let renderString = `${description}: ${chalk.green( complete )}${uncomplete} ${progressNum} \n\n`;
slog(renderString);
} else {
let renderString = `${description}: ${complete}${uncomplete} ${progressNum} \n\n`;
slog(chalk.red(renderString));
}
num++;
} else {
clearInterval(Timer);
parentPort.postMessage(status);
process.exit(0);
}
}, time);
}
複製代碼
// main.js
// .... 省略 ....
let pathString = path.resolve(__dirname, "./Woker.js");
const worker = new Worker(pathString, { workerData: process.stdout.columns });
worker.postMessage({
num: 0,
total: leadTime,
status: true,
time: countDownTime,
});
worker.on("message", (value) => {
if (value) {
console.log(chalk.green(`成功`));
} else {
console.log(chalk.red("失敗"));
}
process.exit(0);
});
// .... 省略 ....
複製代碼
文章首發地址 GitHub