如何在 Electron 主进程准备就绪时仅在一个单例文件中运行函数

huangapple go评论59阅读模式
英文:

How to run a function in a singleton file only when electron main process is ready

问题

Sure, here's the translated content without the code:

我正在使用electron + vue,在我的vue文件中,有一个名为globals.ts的文件,它导出了全局变量,供所有的vue文件使用,它是一个单例文件。我正在尝试让它与electron的主进程通信,以获取用户的机器IP地址,然而,出现了错误,因为显然主进程在globals.ts函数运行之前没有完成加载,这意味着ipcRenderer函数在主进程有API可用之前被调用。我如何让它等待主进程首先完成加载,以便我可以安全地调用函数并获得结果?

(以下为代码部分,不翻译)

希望这有所帮助。

英文:

I am using electron + vue, in my vue files, there is a file called globals.ts which is a file that exports global variables for all my vue files to use, it is a singleton file, I am trying to get it to communicate with electrons main process to fetch the user's machine IP address, however, there are errors occuring because apparently the main process is not finished loading before globals.ts function runs, meaning the ipcRenderer function is invoked before the main has the API available. How do i get it to wait for the main process to finish loading first so i can safely invoke a function and get a result?

//electron/main/main.ts
async function createWindow() {//...code to create window here}
    
ipcMain.handle("getMachineIPv4", (e, arg) => {
  console.log("getting ipv4");
  const interfaces = os.networkInterfaces();
  for (const interfaceName in interfaces) {
    const interfaceInfo = interfaces[interfaceName];
    for (const info of interfaceInfo) {
      if (info.family === "IPv4" && !info.internal) {
        return info.address;
      }
    }
  }
});

.

//electron/preload/preload.ts
    contextBridge.exposeInMainWorld("ipcRenderer", {
  send: (channel: string, data: string) => {
    // whitelist channels
    let validChannels = ["OpenFolder"];
    if (validChannels.includes(channel)) {
      ipcRenderer.send(channel, data);
    }
  },
  receive: (channel, func) => {
    let validChannels = ["fromMain"];
    if (validChannels.includes(channel)) {
      // Deliberately strip event as it includes `sender`
      ipcRenderer.on(channel, (event, ...args) => func(...args));
    }
  },
  invoke: (channel, arg) => {
    //invoke is an async function with a promise
    console.log(channel + " invoked");
    let validChannels = ["asyncFunc", "getMachineIPv4"];
    if (validChannels.includes(channel)) {
      // Deliberately strip event as it includes `sender`
      return ipcRenderer.invoke(channel, arg);
    }
  },
});

.

//inside src folder /assets/globals.ts

 export const address: Promise<string> = (async () => {
  try {
    const hostname: string = await window.ipcRenderer.invoke("getMachineIPv4", "please work");
    const firstThreeNumbers = hostname.split(".")[0];
    const portNumber = "3000";
    const subnet = "10";
    if (firstThreeNumbers === "123") {
      return `123.123.1111.${subnet}:${portNumber}`;
    } else if (firstThreeNumbers === "101") {
      return `123.123.111.${subnet}:${portNumber}`;
    } else if (hostname == "localhost") {
      // Default, usually localhost
      return `123.123.111.${subnet}:${portNumber}`;
    } else {
      console.error("ip address not found");
      return "error";
    }
  } catch (error) {
    console.error("Failed to get address:", error);
    return "error";
  }
})();

答案1

得分: 0

你的问题是globals.ts中的const address立即调用函数表达式,这意味着它会在globals.ts文件被导入时立即执行。解决方法是在address声明的末尾移除调用(;)以将变量转换为函数。

这不会使变量本身成为单例,但你可以通过在文件中使用非导出变量来解决这个问题,以存储一旦检索到的值,并在再次调用函数时重复使用该值。可能看起来像这样:

const address: string | undefined = undefined;

export const getAddress = async (): Promise<string> => {
  if (address) {
    return address;
  }

  try {
    const hostname: string = await window.ipcRenderer.invoke("getMachineIPv4", "works now");
    const firstThreeNumbers = hostname.split(".");
    const portNumber = "3000";
    const subnet = "10";

    if (firstThreeNumbers === "123") {
      address = `123.123.1111.${subnet}:${portNumber}`;
    } else if (firstThreeNumbers === "101") {
      address = `123.123.111.${subnet}:${portNumber}`;
    } else if (hostname == "localhost") {
      // Default, usually localhost
      address = `123.123.111.${subnet}:${portNumber}`;
    } else {
      console.error("ip address not found");
      return "error";
    }

    return address;
  } catch (error) {
    console.error("Failed to get address:", error);
    return "error";
  }
};

然后你可以调用该函数来获取地址,而不是从变量中读取它。当你从渲染器调用该函数时,只能在app.on("ready")之后启动,可以确保你的主进程已经初始化。

import { getAddress } from "./<path>/<to>/globals.ts";

async function doAddressStuff() {
  const address = await getAddress();

  // 更多地址处理 ...
}

希望对你有帮助,如果有不清楚的地方,请告诉我!

英文:

Your problem is that const address in your globals.ts is an Immediately Invoked Function Expression, which means it will get executed as soon as the globals.ts file is imported. The way to solve this, is to remove the invocation (the ();) at the very end of the declaration of address to turn the variable into a function.

This will not make it possible for the variable itself to be a singleton, but you can get around that by using a non-exported variable in the file to store the value once it has been retrieved and re-use that when the function is called again. That could look somewhat like this:

const address: string | undefined = undefined;

export const getAddress = async (): Promise&lt;string&gt; =&gt; {
  if (address) {
    return address;
  }

  try {
    const hostname: string = await window.ipcRenderer.invoke(&quot;getMachineIPv4&quot;, &quot;works now&quot;);
    const firstThreeNumbers = hostname.split(&quot;.&quot;)[0];
    const portNumber = &quot;3000&quot;;
    const subnet = &quot;10&quot;;

    if (firstThreeNumbers === &quot;123&quot;) {
      address = `123.123.1111.${subnet}:${portNumber}`;
    } else if (firstThreeNumbers === &quot;101&quot;) {
      address = `123.123.111.${subnet}:${portNumber}`;
    } else if (hostname == &quot;localhost&quot;) {
      // Default, usually localhost
      address = `123.123.111.${subnet}:${portNumber}`;
    } else {
      console.error(&quot;ip address not found&quot;);
      return &quot;error&quot;;
    }

    return address;
  } catch (error) {
    console.error(&quot;Failed to get address:&quot;, error);
    return &quot;error&quot;;
  }
};

You can then call that function to obtain the address instead of reading it from the variable. When you call the function from the renderer, which can only be started after app.on(&quot;ready&quot;), it is guaranteed that your main process will be initialized.

import { getAddress } from &quot;./&lt;path&gt;/&lt;to&gt;/globals.ts&quot;;
async function doAddressStuff() {
const address = await getAddress();
// more address stuff ...
}

I hope that helps, if anything is unclear, let me know!

huangapple
  • 本文由 发表于 2023年5月26日 14:13:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76338073.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定