您遇到的问题不仅与范围有关,还与代码流有关。这是执行命令的顺序。在您的情况下,console.log(savedData) 在您的侦听器设置变量之前执行。
为什么?因为您的代码不会等到它收到"channel" 事件。仅执行ipcRenderer.on (event, callback); 注册 callback 函数与ipcRenderer。每当收到event 时,就会执行callback。本质上,您可以将其视为一个函数,只需将callback 存储在一个内部变量中,只要设置了值就会返回。
这是异步编程的核心概念:向事件处理程序注册回调以便能够并行运行代码(在您的情况下,callback 将在其他代码空闲时执行)。如果我要说明您案例中的代码流程,我会这样做:
const ipcRend = require('electron').ipcRenderer // (1)
var savedData = null // (2)
ipcRend.on( // (3)
'channel',
(event, data) => { // (5)
savedData = data
console.log(data)
console.log(savedData)
}
)
console.log(savedData) // (4)
首先,执行您的变量声明 (1) 和 (2)。然后,(3),ipcRend.on(...) 紧随其后。这里有一个问题:下一个正在执行的代码是 (4),使用 null 变量 savedData 调用 console.log ();。您的匿名回调函数(大括号内带有(event, data) => {} 的内容)(5) 仅在收到事件"channel" 后执行。这可能随时发生,但很可能不会在您使用 ipcRenderer 注册此回调时发生。
因此,如果您想在执行任何其他代码之前等待回调运行,则可以使用 Promise(在 MDN 上阅读有关 Promises 的更多信息):
const ipcRend = require('electron').ipcRenderer
var savedData = null
await new Promise ((reject, resolve) => {
ipcRend.on(
'channel',
(event, data) => {
savedData = data
console.log(data)
console.log(savedData)
resolve ();
}
)
});
console.log(savedData)
这样console.log (savedData);只会在回调函数运行后执行。
但是,这不仅笨拙,而且也不完全是最佳实践。由于您已经在处理异步编程,因此最好调用另一个函数,将您收到的数据作为参数,而不是将其存储在全局范围内。这种方式更容易理解,也更容易长时间维护:
function processReceivedData (data) {
console.log (data);
// Anything else you want to do with this data...
}
const { ipcRenderer } = require ("electron");
ipcRenderer.on ("channel", (event, data) => { processData (data); });
看到了吗?更干净,更易于理解,并且没有更多的全局变量!当然,有时只需要全局变量。但是要确保没有同步代码依赖于变量值,这些变量值只会在任何异步事件发生后设置。
作为脚注:在导入的声明中使用{},就像我上面使用的那样,会自动将加载的模块“解包”到所需的变量中。这样一来,你就必须在你想使用它的时候写ipcRenderer(我知道这更长),但是使用这种方法不太容易出错,因为你不必写require().<submodule you actually want>。此外,您可以一次解压缩多个子模块:const { ipcRenderer, contextBridge } = require ("electron"); 一次加载两个模块并将它们存储在ipcRenderer 和contextBridge 中。