1

I am writing an Electron application. In the renderer process, there is a processing of an event.

The event can be triggered multiple times in such a way they may overlap. So the need is to stack and sequentially process the event instances, if they overlap.

ipcRenderer.on('doWork', function (event, args) {
   doWork();
});

As the goal is to prevent overlapping of doWork executions, have tried to make it a Promise in such a way that it executes, and if more events come in they pile up. But I am failing to build the algorithm.

Other questions that I've found on stackoverflow are always about a pre-defined/already known number of Promises (sorry if I've missed something).

Edit: questions like:

How can I execute array of promises in sequential order?

Execute many promises sequentially (Concept)

Inside doWork I am calling electron's desktopCapturer.getSources which is async. Then I am saving to the file system, and ensuring a maximum number of screenshots is saved, but these are using the sync Nodejs operations.

Robson Hermes
  • 425
  • 5
  • 12
  • 1
    It is possible to include links to the "other" Stack Overflow questions? – Maxime Launois Jul 10 '19 at 13:35
  • 1
    `doWork` execution will never overlap, unless it is itself calling an asynchronous API. But unless that is true, it will keep JS busy and no new event will get processed until `doWork` has completed. So you can see it is important to provide details about `doWork`. – trincot Jul 10 '19 at 13:37
  • Stacking promises is exactly what you want to avoid. Consider using Observables instead – papastepan Jul 10 '19 at 13:37
  • Search for the term *queuing* promises – Bergi Jul 10 '19 at 13:40
  • @MaximeLaunois yes, you can flag the question as a possible duplicate of another question – GrafiCode Jul 10 '19 at 13:45
  • @trincot Indeed. Inside `doWork` I was calling electron's `desktopCapturer.getSources` which is async, works with callbacks. The rest of methods I already synched. Basically I am taking screenshots of the app, saving in local filesystem and finally ensuring a maximum number of files were kept. – Robson Hermes Jul 10 '19 at 13:48

1 Answers1

1

Assuming that you succeeded in making doWork() return a Promise, you could chain the promises that each of these calls return:

let promise = Promise.resolve();
ipcRenderer.on('doWork', function (event, ...args) {
   promise = promise.then(doWork);
});

If the arguments must be passed to doWork:

let promise = Promise.resolve();
ipcRenderer.on('doWork', function (event, ...args) {
   promise = promise.then(() => doWork(...args));
});

Do realise however that if the rate of events is higher than what doWork can process, your chain will get longer and longer. If all these calls really have to be made, this is not really a problem.

But in some scenario's it might be acceptable and more interesting to skip calls and just perform the "last" one. If that is an option, then look into one of the many "debounce" implementations. Alternatively, here is an idea to skip calls when a more recent request comes in:

let promise = Promise.resolve();
let pendingArgs = null;
ipcRenderer.on('doWork', function (event, ...args) {
    const isPending = pendingArgs !== null;
    pendingArgs = args; // doWork should be called with the latest version of args
    if (isPending) return; // There is already a pending request to doWork
    promise = promise.then(() => {
        const args = pendingArgs;
        pendingArgs = null;
        return doWork(...args);
    });
});
trincot
  • 317,000
  • 35
  • 244
  • 286
  • thanks, but on this case after the first occurrence of doWork, the promise will be settled, and a second occurrence will not get executed. Maybe I should try to synchronize elsewhere. – Robson Hermes Jul 10 '19 at 14:07
  • Sure, the second one will get executed, mainly *because* the promise is settled. That is why we even *start* with a settled promise. Note that each `then` call creates a *new* promise that is assigned to the `promise` variable, so you get many settlements. Did you try it? – trincot Jul 10 '19 at 14:11
  • Yes I did, still trying to see if I am missing anything here, thanks again. – Robson Hermes Jul 10 '19 at 14:18
  • What I mean with my first comment is that the second incoming event arrived _after_ the previous Promise was settled (the one resulted from first event handling). Guess that's why the second one is not processed. Believe I better take a step back and recheck my design. – Robson Hermes Jul 10 '19 at 14:28
  • But when a promise is settled, the `then` callback is guaranteed to execute on it (if if the promise settled *long* before), so `doWork` *will* be called then. I don't see how you are reasoning that it would *not* be called. I tested this code, and it behaves as I intended it. If you have a fiddle that shows what you are getting, I will be pleased to have a look at it. – trincot Jul 10 '19 at 14:32
  • NB: make sure that in the `then` callback you *return* the promise returned by `doWork()`. In the first two code blocks in my answer this `return` is implicit because of the expression syntax, but if you have a code block there, make sure to use `return doWork()`. – trincot Jul 10 '19 at 15:05