代码之家  ›  专栏  ›  技术社区  ›  Vishal

为什么我的ui线程被阻塞,即使我使用promise?

  •  0
  • Vishal  · 技术社区  · 6 年前

    我正在读取用户选择的一些.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');
                }
            });
        });
    

    正如您在代码中看到的,在选择了文件之后,我正在调用fromfileasync函数,该函数返回一个promise。据我所知,承诺不会阻止来电者。那么为什么在我这边,代码会阻塞我的ui,直到创建了一个新文件?

    1 回复  |  直到 6 年前
        1
  •  0
  •   trincot Jakube    6 年前

    promises不提供并行js执行,它们只提供异步执行。此外,promise解析事件通常被添加到微任务队列中,作为正在进行的任务的一部分使用。

    另一方面,与gui相关的事件放在任务队列中,只有当微任务队列为空时,才会为其提供服务。

    因此,如果承诺立即解决,则解决事件将通过触发 then 在处理任何gui事件之前进行回调。这可以给你一个阻塞的体验。

    这里有一段代码演示了这种阻塞行为:它创建了一个0毫秒的短超时,但首先触发了一个持久的承诺链,每个承诺都会立即解决。它们阻止计时器触发其回调。只有当承诺链完成时,计时器才能轮到它:

    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 输出本身也会延迟(原因相同),但从时间戳(以毫秒为单位)可以看出,承诺链负责计时器获得其轮值的长延迟。