为何我的async/Promise方法同步运行?(Electron/Node.js场景)
问题分析与解决方案
你的核心问题在于XML解析是CPU密集型的同步操作,加上代码里对异步函数的调用方式有误,导致任务始终按顺序阻塞执行,和数据大小无关。以下是具体问题拆解和修复方案:
一、当前代码的两个关键问题
异步函数未被正确等待
在end事件回调里调用saveObj时没有加await,导致回调函数会直接跳过等待解析完成,先执行file_counter++,不过这不是解析顺序固定的主因。同步解析阻塞单线程
parser.parse(data)是同步方法,哪怕你把它放进setTimeout,一旦执行就会占用Node的主线程直到解析完成。因为JS是单线程模型,所有CPU密集型任务都会阻塞事件循环,所以不管数据大小,解析任务会按队列顺序执行,先到的先完成,自然输出顺序是0、1、2...N。
二、修复步骤
1. 先修正异步函数调用
给saveObj调用加上await,确保当前连接的处理流程等待解析完成后再推进计数器,避免日志混乱:
connection.on('end', async function() { console.log("client " + file_counter + " disconnected"); await saveObj(content, file_counter); // 添加await console.log("returned from " + file_counter); file_counter++; });
2. 用Worker Threads解决CPU阻塞问题
大XML解析属于CPU密集型任务,必须放到单独线程中执行,避免阻塞主线程处理新TCP连接。Node的worker_threads模块可以实现这个需求:
第一步:新建解析工作线程文件xml-parser-worker.js
const { parentPort } = require('worker_threads'); const { XMLParser } = require("fast-xml-parser"); const parser = new XMLParser(); parentPort.on('message', (data) => { try { const jObj = parser.parse(data.content); parentPort.postMessage({ success: true, cnt: data.cnt }); } catch (err) { parentPort.postMessage({ success: false, cnt: data.cnt, error: err.message }); } });
第二步:修改主文件,用Worker处理解析
var net = require('net'); const { Worker } = require('worker_threads'); var file_counter = 0; var server = net.createServer(function(connection) { console.log('Client Connected'); // 优化:用Buffer收集数据,避免大字符串拼接性能问题 const chunks = []; connection.on('data', function(data) { chunks.push(data); }); connection.on('end', async function() { const currentCnt = file_counter; console.log("client " + currentCnt + " disconnected"); const content = Buffer.concat(chunks).toString(); await parseXMLWithWorker(content, currentCnt); console.log("returned from " + currentCnt); file_counter++; }); connection.write('y'); }); // 封装Worker调用逻辑 function parseXMLWithWorker(data, cnt) { return new Promise((resolve, reject) => { const worker = new Worker('./xml-parser-worker.js'); worker.postMessage({ content: data, cnt: cnt }); worker.on('message', (msg) => { if (msg.success) { console.log("Parsed content number " + msg.cnt); resolve(); } else { console.error("Parse failed for content " + msg.cnt + ": " + msg.error); reject(new Error(msg.error)); } worker.terminate(); }); worker.on('error', (err) => { console.error("Worker error for content " + cnt + ": " + err.message); reject(err); worker.terminate(); }); }); }
三、效果说明
修改后,XML解析任务会在独立线程中执行,主线程可以继续处理新的TCP连接。不同大小的XML解析会根据实际耗时完成,输出日志的顺序不再是严格的0、1、2...,而是谁先解析完谁先输出。
内容的提问来源于stack exchange,提问作者JohnTortugo
相关产品推荐
相关产品推荐

