为什么即使使用诺言,我的ui线程仍被阻止?

时间:2018-11-03 00:27:27

标签: javascript electron

我正在读取用户选择的一些.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,直到创建了一个新文件?

1 个答案:

答案 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输出本身的可视化也会被延迟(出于相同的原因),但是您可以从时间戳(以毫秒为单位)中看到,承诺链造成了计时器轮到它了。