parro-it/libui-node

setTimeout and setInterval are not executed unless some user action trigger UI events.

RockyF opened this issue ยท 10 comments

I just use setInterval to update a label for counting, but it can't refresh unless the mouse moves on the window.

What OS and version are you using?

What OS and version are you using?

Doesn't matter, I can reproduce the error on all platforms,. Thanks to point this out!

It seems that timers doesn't trigger the libuv background fd as IO operations does.
I'm currently improving the event loop on separate branches, but I cannot find a way to take timers into account.

One workaround could be to patch setTimeout and setInterval using libuv timer (when they get merged in libuv).

This doesn't work at all (not even when moving the mouse)

var libui = require('libui-node');

const fmt = ms => `${Math.floor(ms/60000)}:${Math.floor((ms/1000)%60)}.${ms%1000}`;

var win = new libui.UiWindow('Timer', 320, 320, false);
var box = new libui.UiVerticalBox();
win.setChild(box);

var start = new Date((new Date()).getTime() + 10*1000);

var label = new libui.UiLabel();
label.text = fmt(start - new Date());
box.append(label, true);

var int = setInterval(() => {
	label.text = fmt(start - new Date());
}, 1000);

win.onClosing(function () {
	clearInterval(int);
	win.close();
	libui.Ui.quit();
});

win.show();

libui.Ui.main();

That's because you're calling main, and the node loop is then com pletely locked. You should try the core example in mac_os_loop branch.

I was able to make timers work (not tested on mac yet).
But, we have to choose a minimum time granularity.

That's because I check in background thread if there are any timer callback pending after a blocking call to check pending IO callbacks.

So, if we set that blocking call to timeout after e.g. 1s, timers callback are run at most every second (independently by the timeout on js side).

The lower we can set that timeout the higher timer precision we get, but we'll pay a performance penalty, even if timers are never used.

Actually, I set it to 100ms, but we can tune a better value I guess...

Relevant code:

https://github.com/parro-it/libui-node/blob/next-loop/src/arch/unix/libui_loop.cc#L31

The core-api example now run a setInterval that updates a label, and an http server that do the same everytime is called.

We'll have to check other kind of async node calls to check that they work fine, because they doesn't relay on the same event loop queue as IO:

It's better but not optimal:

setInterval(() => {
	var remaining = start - new Date();
	console.log(sec(remaining))
}, 50);

prints:

9.767
9.459
9.149
8.835
8.522
8.213
7.907
7.593

(So the callback is run every ~400ms but is should be 50ms)

Works correctly if the mouse is moved continuously.

I created an example to test the event loop: https://github.com/parro-it/libui-node/blob/next-loop/examples/event-loop.js

As you'll see, timer are not performing really well, but if I lower background timeout, the UI became unresponsive.

Other kind of async stuff seems to work well, so the plan is to replace setTimeout and setInterval with a n implementation based on libui timers, when they'll land on master.

@RockyF would you mind checking the new pre-release candidate 0.2.0-rc1?
This issue should be solved in that release.

@parro-it sure!