#I need a way to ensure that ipc calls happen sequentially

1 messages · Page 1 of 1 (latest)

proud dove
#

The first question is what initiates the setup tasks? User interaction with something in the window or simply the window creation?

#

My solution would be to have

async function startup() {
  // show a spinner or something on the window
  await window.electronApi.[your IPC function]
  // remove spinner
}
document.addEventListener('DOMContentLoaded', () => {
  console.log('DOM loaded')
  startup()
})
#

oops, should have said that would be in renderer.js

#

You don't have the un-minified version?

#

But before it's bundled you have the un-minified version to make edits, right?

#

As Team Vanilla I'm unfamilar with Typescript file extensions but wouldn't App.tsx be what becomes renderer.js?

#

Ah. You could move the whole shebang into main.js (or React/Typescript equivilent) if you have access to that. When you spawn the window you could then set the spinner using a webContents.on(), await your tasks, then remove the spinner. User could still move the window arround, you could even have a progress bar that you update after each task.

#

Ok, I see you put that code up there, sorry I missed it. I have some similar code that spawns something. Give me a minute.

#

I think this is my equivalent (though this is from a node app rather than Electron but same principal.

exports.getFileExif = async (request) => {
    if (request.file) {
        // console.log(`exiftool -json -l -g0:1 ${mover2.rootFileSystem}/${request.folder}/${request.file}"`)
        const filePath = path.join(process.cwd(), 'modules/')
        const fileExec = `${filePath}exiftool.exe -json -l -g0:1 "${mover2.rootFileSystem}/${request.folder}/${request.file}"`
        try {
            const { error, stdout, stderr } = await exec(fileExec)
            return { success: true, exif: JSON.parse(stdout) }
        } catch (e) {
            // console.log(e)
            console.log(fileExec)
            return { success: false, error: { status: '599', statusText: e.message } }
        }
    } else {
        return { success: false, error: { status: '590', statusText: 'No file specified' } }
    }
}

I'm able to await that from index.js which is standing in for main.js in Electron's case. I just await getFileExif() and unless git behaves differently than exiftool I think that wouldn't resolve early.

#

Would using exec() instead of spawn() work for you?

#

Well all I can promise is that my code never returns until exiftool is finished which can take a bit of time to simply spin up (I have since moved to a pool since exiftool has a keepalive option) and if the image is an heic or other overstuffed type it can take a while to sift through all the metadata yet the await holds.

#

3:35am here as I'm doing a different project parsing through thousands of line of JSON that power file copying, directory creating, database inserts, etc. 😛 just wanted to take a little break. Get your rest, try subbing in my try block in place of your current return.

marsh ravine
#

probably because this involves communication between the main process and the renderer process.
It appears that IPC is designed specifically to enforce that you can never expect anything to happen in any predictable order. I'm at this point 100% convinced that the developers have put in code to specifically prevent people from doing what I am doing.
This has gone far beyond anything that could be a "mistake" and moved into the territory of full-on malice on the part of the maintainers.

inland osprey
#

Send from renderer to main, with timestamp, queue tasks on main, delayed by arbitrary number of milliseconds of your liking. Sort queue by timestamp before polling it

Not the most graceful, but it’s the only way to ensure sequential items via IPC

proud dove
#

Sorry to hear that. I take it you console.log() error, stdout and stderr and that appeared later as the main process continued on to the next thing?

marsh ravine
inland osprey
#

that doesn't make any sense, i am glad they didnt build it the way you want it

marsh ravine
#

But fine, please enlighten me, how do you expect me to iterate a filesystem that doesn't exist yet?

inland osprey
#

i abuse the living heck out of IPC for a living

#

i already gave a proposed solution and you called it moronic

marsh ravine
#

yeah, because in normal javascript land, it's completely normal to expect to be able to return a promise, do some stuff asynchronously, and then have that promise resolve, but no, electron says "let's resolve the promise when we want to, not when the programmer does"

inland osprey
#

just so i understand, you want to ensure that you can do:

renderer -> main (index 0)
renderer -> main (index 1)

asynchronously from the renderer, but you also want index 0 to ensure it completes before index 1?

inland osprey
marsh ravine
#

Yes, and I've tried dozens of different methods, including using the legacy method of creating a promise, firing a message to main, having main start its thing, wait for that task to complete, then send a message back to the renderer, which I catch as an event and use to cause my promise to resolve AND IT STILL RESOLVES EARLY

inland osprey
#

i have never had this issue

#

i would have to see the code

#

like i said, i abuse IPC for a living

#

i have lead electron development at medal.tv for 6 years

marsh ravine
inland osprey
#

typically i would write something like this in an event-driven order

#

where does ipc from renderer -> main get called

marsh ravine
#

please also bear with the increasingly deranged console logs, I've been debugging this for the better part of the last 3 days.

inland osprey
#

lol, trust me ive been there

marsh ravine
#

it's a miracle I still have any hair on my head. 😅

inland osprey
#

ty lemme grok this real quick

marsh ravine
#

no prob, I've tried to keep things relatively close together, but this thing has turned into a bit of a juggernaut

inland osprey
#

which ipc calls/channels are you using in this case, the ones you want to be sequential

marsh ravine
#

So, the goal is that really only magian:legacy:installAshita needs to actually be sequential. It has to run after magian:ensureGit, and before all the others. things like magian:ensureProfiles, ashita:getPlugins, ashita:getPolPlugins, and ashita:getAddons can all run in whatever order, as long as they happen after magian:legacy:installAshita completes.

EDIT: holy wall of text batman

inland osprey
#

hmm

marsh ravine
inland osprey
#

you are almost there

#

one thing that stands out is that magian:legac:installAshita is called via ipcRenderer.send. you mentioned you had tried other ways -- you already tried ipcRenderer.invoke like the others?

marsh ravine
#

Yep, that was most of yesterday

#

The day before was spent trying to do it in main.ts without blocking the main thread and causing a "Magian Launcher is not responding"

inland osprey
#

i am having a hard time understanding the order of operations that lead up to calling install. is this done by the user clicking an install button or something?

marsh ravine
#

It's triggered when react is mounted

inland osprey
#

are you showing a splash screen or some intermittent state at that time

marsh ravine
#

yeah

quick hemlock
#

I'm a little bit far away dev now, but in the past I solve a sequence problem by writing EventBus, Sequence and Step classes each step when finished send a next event and so on, the EventsBus was the common bus of all the events. Don't know if this philosophy could help?

marsh ravine
#

src/Components/Launcher.tsx:23-32, if that helps any

inland osprey
#

i wish i had my ipc stuff open sourced, hoping to do it later this year, would be so much easier to explain how i would solve this lol

marsh ravine
#

relatable.

inland osprey
#

in my app something like this would go through my IPC wrapper, where i'd do something like this:

in main i would listen for:

GlobalEvents.on('install', async () => {
   // do the async stuff
   GlobalEvents.emit('install:complete')
})

and then in the renderer (and in other places in main) i would listen for install:complete event to trigger other actions

#

my GlobalEvents class basically propagates events to every renderer and main

#

so your current "legacy" approach is actually closer to how i would approach this

#

rather than trying to handle invocation timing and sequential IPC events, i would just make it event-driven so that things dont fire unless the thing before it calls it

#

the others seem they can use invoke/handle

marsh ravine
#

they definitely can. I'm still confused though as to why the promise I'm getting back from the renderer side is resolving before it gets the message from the main side that would cause it to resolve.

#

Like, I know it's a silly thing to get stuck up on, but it just doesn't sit right, y'know?

inland osprey
#

perhaps try something like this in your renderer component:

    try{
      window.ipcRenderer.newUpdateAshita()
      window.ipcRenderer.onUpdateAshita(() => 
        // post-install renderer code
      })
    } catch (e) {
      console.log(`aargh: ${e}`)
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

and then added to preload:

      ipcRenderer.on('magian:legacy:installAshita:reply', (_) => {
        callback()
      })
    }
marsh ravine
#

I'll give that a shot, thank you

inland osprey
#
  • remove the ipcRenderer.on from newUpdateAshita
marsh ravine
inland osprey
#

if not _ then prefixed the arg with _ xD