【问题标题】:Node .on method firing too many timesNode .on 方法触发太多次
【发布时间】:2019-02-06 05:34:38
【问题描述】:

我有一个 Electron 应用程序,它向用户显示目录列表。当用户单击一个按钮时,我的界面脚本 interface.js 会清除容器 div 并向 main.js 发送一条消息。收到消息后,main.js 将目录扫描成一个文件名数组,并将该数组作为响应返回给 interface.js。 Interface.js 使用 .on 方法,在收到响应时触发并使用数组的内容更新容器 div。

这是我第一次真正尝试使用 Node,就界面行为而言,一切都非常出色!太棒了,才几个小时,我已经爱上了 Node!

但是,在调试/压力测试时,我将 .on 方法中返回的数组打印到控制台,并注意到一些奇怪的行为。用户第一次单击按钮时,.on 方法运行一次(通过向控制台发送一条消息进行验证)。用户第二次点击,该方法运行两次(通过向控制台发送两条消息验证);第三次运行三次,以此类推。

main.js中扫描目录的函数每次点击只运行一次,所以问题一定在interface.js中。

我的 main.js 和 interface.js 代码:

main.js:

const {app, BrowserWindow, ipcMain} = require('electron');
const fs = require('fs');

...

ipcMain.on( 'list-directory', ( event, directory ) => {
    var files = fs.readdirSync( directory );
    event.sender.send( 'list-directory-reply', files );
});

interface.js

var { ipcRenderer, remote } = require( 'electron' );  
var main = remote.require( "./main.js" );

...

button.addEventListener('click', function(){ showDialogue( this ); }, false );

...

showDialogue( select ) {
    // clear the dialogue
    // some other stuff
    ipcRenderer.send( 'list-directory', './files/documents/' );
    ipcRenderer.on( 'list-directory-reply', function( event, contents ) {
        console.log( contents );
        if ( contents.length > 0 ) {
            // add contents to the dialogue
        }
    } );
}

代码改编自 Electron 网站上的教程。

为什么ipcRenderer.on 会运行多次?是否有可能每次单击按钮时都绑定到某些东西,因此运行次数与过去的单击次数一样多?我在事件侦听器函数和ipcRenderer 之前的showDialogue 函数中放置了一个打印语句,但它们每次点击都只打印一次,所以重复肯定只来自ipcRenderer.on

【问题讨论】:

  • 您在每次点击按钮后订阅ipcRenderer.on,这会导致多次订阅。尝试在 click 事件之外定义 ipcRenderer.on 事件处理程序,它应该可以正常工作。
  • @planet_hunter 太棒了!完美运行。请添加为答案,以便我接受

标签: javascript node.js electron


【解决方案1】:

您在每次点击按钮后订阅ipcRenderer.on,这会导致多次订阅。尝试在 click 事件之外定义 ipcRenderer.on 事件处理程序,它应该可以正常工作。

这样的——

button.addEventListener('click', function(){ showDialogue( this ); }, false );


ipcRenderer.on( 'list-directory-reply', function( event, contents ) {
    // ipcRenderer event handler
});

showDialogue(select) {
    ipcRenderer.send( 'list-directory', './files/documents/' );
}

【讨论】:

    【解决方案2】:

    正如@planet_hunter 所提到的,每次调用showDialogue() 时,您都会设置一个侦听器。您需要删除侦听器或将侦听器移到调用函数之外。

    但是,我发现一个更简洁的解决方案是使用.once 命令。这与.on 的行为类似,但无需手动删除.on 侦听器(您还没有这样做),它会自行删除。

    showDialogue( select ) {
        // clear the dialogue
        // some other stuff
        ipcRenderer.send( 'list-directory', './files/documents/' );
        ipcRenderer.once( 'list-directory-reply', function( event, contents ) {
            console.log( contents );
            if ( contents.length > 0 ) {
                // add contents to the dialogue
            }
        } );
    }
    

    【讨论】:

    • 尽管在这种情况下once 可能是一种可能的解决方案,但最好在代码开始执行之前将事件处理程序保留在适当的位置。例如,在这种情况下,错过第一个'list-directory-reply' 事件的可能性很高,因为我们首先调用list-directory 事件(触发list-directory-reply 事件)但稍后订阅list-directory-reply 事件。
    • @planet_hunter 如果需要在ipcRenderer回复代码中处理通过初始函数调用传递的参数值,使用.once方法可以吗?例如,直接使用上面的代码,假设ipcRenderer 回复代码中需要select 参数。通过主进程汇集参数并返回到渲染进程似乎有点混乱。
    • @skaz 有时事件会被触发多次。我无法解决问题。如果你能在这种情况下提供帮助,我将是一个很大的帮助。这是票 [stackoverflow.com/questions/63906065/…
    猜你喜欢
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    • 2015-02-10
    • 1970-01-01
    • 2022-01-06
    相关资源
    最近更新 更多