我正在读取用户选择的一些.dbf文件,并将该数据写入Excel文件。我正在为此目的使用电子。我的代码工作正常,但阻塞了主线程一段时间。这是我的代码:
ipcMain.on('create-one-file', (event, reportName, additionalColumns = [], startingRowNo = 5) => {
// show dialog to choose .dbf files that user wants to write in GST report
dialog.showOpenDialog({
filters: [
{ name: 'CSV Files', extensions: ['csv'] }
],
properties: ['openFile', 'multiSelections']
}, (fileNames) => {
if (fileNames) {
event.sender.send('create-one-file-loading-message', 'Reading from template');
// read the template file
XLSX.fromFileAsync(path.join(__dirname, `../sample-files/${reportName}.xlsx`)).then((workbook) => {
let rowIndex = 0;
let count = 0;
// loop through all the files
async.whilst(
() => count < fileNames.length,
callback => { // this function will be called on every iteration
// start parsing dbf file
const parser = new Parser(fileNames[count], { encoding: 'utf-8' });
let worksheet = null;
let noOfColumns = 0;
parser.on('start', () => {
event.sender.send('create-one-file-loading-message', `writing ${path.parse(fileNames[count]).name.toLowerCase()} report`);
// reset row no. as new sheet is being written
rowIndex = 0;
// select the sheet to work on
worksheet = workbook.sheet(path.parse(fileNames[count]).name.toLowerCase());
if (worksheet) {
// get total columns in the worksheet
noOfColumns = (worksheet.row(3) && worksheet.row(3)._node
&& worksheet.row(3)._node.children && worksheet.row(3)._node.children.length
&& worksheet.row(3)._node.children.length - 1) || 0;
}
});
parser.on('record', (record) => {
if (worksheet) {
let cells = [...additionalColumns, ...Object.values(record)];
cells.shift();
cells.shift();
let isNull = true;
cells.forEach((cell, columnIndex) => {
if ((columnIndex + 1) < noOfColumns && cell) {
isNull = false;
}
});
if (!isNull) {
rowIndex = rowIndex + 1;
cells.forEach((cell, columnIndex) => {
if ((columnIndex + 1) < noOfColumns) {
if (!cell || cell === "NaN") cell = "";
worksheet.row(rowIndex + startingRowNo - 1).cell(columnIndex + 1).value(cell);
}
});
}
}
});
parser.on('end', () => {
count++;
callback(null);
});
parser.parse();
},
err => {
if (err) {
event.sender.send('create-one-file-error', err.message, 'Error reading reports');
} else {
event.sender.send('create-one-file-loading-message', 'Finishing it up');
workbook.toFileAsync(`D:/${reportName}.xlsx`).then(() => {
event.sender.send('create-one-file-success', `Successfully created file at D:/${reportName}.xlsx. Click this message to see the file`);
}).catch((error) => {
event.sender.send('create-one-file-error', error.message, `Error writing file. Please make sure that D:/${reportName}.xlsx is closed`);
});
}
}
);
}).catch((err) => {
event.sender.send('create-one-file-error', err.message, 'Error reading template');
});
} else {
event.sender.send('create-one-file-cancelled');
}
});
});
正如您在代码中看到的那样,选择文件后,我正在调用FileAsync函数,该函数返回一个Promise。据我所知,promise不会阻止呼叫者。那为什么在我这边,代码阻塞了我的UI,直到创建了一个新文件?
答案 0 :(得分:0)
Promise不提供并行JS执行,它们仅提供异步执行。此外,承诺解决事件通常添加到微任务队列中,并作为正在进行的任务的一部分使用。
另一方面,与GUI相关的事件被放入任务队列中,仅在微任务队列为空时才得到服务。
因此,如果Promise立即解决,则在触发任何GUI事件之前,将通过触发相关的then
回调来处理解决事件。这就是可以提供阻止体验的原因。
这是一段代码,演示了这种阻塞行为:它创建了0ms的短暂超时,但首先触发了一个长久的承诺链,每个承诺都会立即解决。它们阻止计时器触发其回调。只有当承诺链完成时,计时器才能轮到:
setTimeout(() => console.log(Date.now(), 'timer expired'), 0);
function loop(i) {
if (i <= 0) return; // end the chain
Promise.resolve().then(_ => loop(i-1));
}
console.log(Date.now(), 'start');
loop(1000000);
console.log(Date.now(), 'Async promise chain has started...');
请注意,console.log
输出本身的可视化也会被延迟(出于相同的原因),但是您可以从时间戳(以毫秒为单位)中看到,承诺链造成了计时器轮到它了。