TypeScript CLI程序控制台日志覆写异步与并发问题求助
解决TypeScript CLI进度条异步覆写混乱的问题
1. 用互斥锁或任务队列同步控制台操作
Node.js没有原生互斥锁,但可以通过第三方库或自定义队列保证同一时间只有一个操作修改stdout,避免异步调用导致的光标顺序混乱。
方案一:使用async-mutex库实现互斥
import { Mutex } from 'async-mutex'; // 全局互斥锁,控制所有stdout进度更新操作 const stdoutMutex = new Mutex(); async function updateProgress(emoji: string, message: string) { // 获取锁,等待前面的操作完成 const releaseLock = await stdoutMutex.acquire(); try { // 将光标移到行首,覆盖当前行内容 process.stdout.write('\r'); // 写入新的进度内容 process.stdout.write(`${emoji} ${message}`); } finally { // 释放锁,允许后续操作执行 releaseLock(); } }
方案二:自定义任务队列(无第三方依赖)
如果不想引入额外库,可以手动实现一个串行任务队列:
let isProcessingQueue = false; const taskQueue: (() => Promise<void>)[] = []; // 将控制台操作加入队列,串行执行 async function enqueueConsoleTask(task: () => Promise<void>) { taskQueue.push(task); if (!isProcessingQueue) { isProcessingQueue = true; while (taskQueue.length > 0) { await taskQueue.shift()!(); } isProcessingQueue = false; } } // 封装进度更新函数 async function updateProgress(emoji: string, message: string) { await enqueueConsoleTask(async () => { process.stdout.write('\r'); process.stdout.write(`${emoji} ${message}`); }); }
2. 处理多位置内容更新
如果需要在同一行更新多个动态元素(比如两个进度条),可以用cursorTo定位光标,配合clearLine清除残留内容:
async function updateMultiProgress(emoji1: string, p1: number, emoji2: string, p2: number) { const releaseLock = await stdoutMutex.acquire(); try { // 移到行首 process.stdout.cursorTo(0); // 写入第一个进度 process.stdout.write(`${emoji1} Task 1: ${p1}%`); // 定位到指定列,开始写入第二个进度 process.stdout.cursorTo(30); process.stdout.write(`${emoji2} Task 2: ${p2}%`); // 清除光标到行尾的内容,避免旧文本残留 process.stdout.clearLine(1); } finally { releaseLock(); } }
3. 异步场景下的使用规范
所有进度更新操作必须通过上述同步函数调用,包括Promise回调、定时器等异步代码中,不能直接调用process.stdout.write:
// 示例:异步任务中更新进度 async function runLongTask() { for (let i = 0; i <= 100; i++) { const emoji = i < 33 ? '⏳' : i < 66 ? '🚀' : '✅'; await updateProgress(emoji, `Processing: ${i}%`); await new Promise(resolve => setTimeout(resolve, 50)); } // 任务结束后换行,避免命令行提示符和进度条重叠 process.stdout.write('\n'); }
额外注意事项
- 非进度类日志(比如错误信息)要输出到
process.stderr,避免干扰stdout的进度覆写。 - 确保所有涉及
stdout的进度操作都经过同步层,遗漏的直接调用会再次导致顺序混乱。
内容的提问来源于stack exchange,提问作者Devix
相关产品推荐
相关产品推荐

