Вероятно, есть несколько способов избежать блокировки главного l oop, но, вероятно, лучше всего зависит от того, какое событие приведет к нарушению while
l oop.
Если вы действительно нуждаются в "опросе" некоторого условия, я думаю, что простейшим подходом может быть тайм-аут l oop:
function myPollingFunc(arg1, arg2) {
if (the_event_occured) {
// Here is where you will invoke whatever it is you want to do
// when the event occurs, then destroy the source since the
// condition has been met.
this.classMethod(arg1, arg2);
return GLib.SOURCE_REMOVE;
}
// If the condition was not met, you can wait until the next loop and
// check again.
return GLib.SOURCE_CONTINUE;
}
// Probably you'll want to store this ID (maybe as a property on your class),
// so you can destroy the source if the DBus service is destroyed before the
// event you're waiting for occurs.
let sourceId;
sourceId = GLib.timeout_add_seconds(
// Generally this priority is fine, but you may choose another lower one
// if that makes sense
GLib.PRIORITY_DEFAULT,
// The timeout interval of your loop. As described in the linked article,
// second-based loops tend to be a better choice in terms of performance,
// however note that at least one interval will pass before the callback
// is invoked.
1,
// Your callback function. Since you're probably creating the source from
// a class method and intend on modifying some internal state of your class
// you can bind the function to the class scope, making it's internal state
// and other methods available to your callback.
//
// Function.bind() also allows you to prepend arguments to the callback, if
// that's necessary or a better choice. As noted above, you'll probably want
// to store the ID, which is especially important if you bind the callback to
// `this`.
//
// The reason is that the callback will hold a reference to the object it's
// bound to (`this` === `Foo`), and the source will hold the callback in the
// loop. So if you lose track of the source and the ability to destroy it, the
// object will never be garbage collected.
myPollingFunc.bind(this, arg1, arg2)
);
Вы можете сделать то же самое с незанятым источником, который ждет, пока не будет приоритетное событие ожидает, а не фиксированное время ожидания. Подвох с незанятыми источниками в том, что если нет других ожидающих событий, ваш обратный вызов будет повторно вызываться почти так же быстро, как while
l oop, и вы, вероятно, истощите основной l oop (например, сделайте его другим событиям трудно войти в дверь).
С другой стороны, может быть более простой способ сделать это, если вам на самом деле не нужно опрашивать условие, но вы в ожидании сигнала или изменения свойства GObject:
...
_runLoop () {
// Waiting for some object to emit a signal or change a property
let handlerId = someObject.connect('some-signal', () => {
someObject.disconnect(handlerId);
this.classMethod(arg1, arg2);
});
let propId = someGObject.connect('notify::some-prop', () => {
if (someGObject.some_prop === 'expected_value') {
someGObject.disconnect(propId);
this.classMethod(arg1, arg2);
}
});
}
...
Не зная, какого типа событие или условие вы ожидаете, сложно дать лучший совет относительно лучшего решения, но, возможно, это поможет. В общем, я бы сказал, что если вы считаете, что хотите l oop при каком-то условии, вам лучше проверить это условие в существующей GLib mainl oop, чем создавать свой собственный sub-l oop.
РЕДАКТИРОВАТЬ
С большим контекстом кажется, что вы будете полагаться на внешний процесс, так или иначе. В этом случае я настоятельно рекомендую избегать низкоуровневых функций порождения в GLib и вместо этого использовать GSubprocess.
Это гораздо более безопасный выбор для высокоуровневых привязок к языку, а также включает вспомогательные функции, написанные в C что вы, скорее всего, в конечном итоге переписаете:
onProcExited(proc, result) {
try {
proc.wait_check_finish(proc, result);
} catch (e) {
logError(e);
}
}
onLineRead(stdout, result) {
try {
let line = stdout.read_line_finish_utf8(result)[0];
// %null generally means end of stream
if (line !== null) {
// Here you can do whatever processing on the line
// you need to do, and this will be non-blocking as
// all the I/O was done in a thread.
someFunc(line);
// Now you can request the next line
stdout.read_line_async(null, onLineRead.bind(this));
}
} catch (e) {
logError(e);
}
}
startFunc() {
this._proc = new Gio.Subprocess({
argv: ['proc_name', '--switch', 'arg', 'etc'],
flags: Gio.SubprocessFlags.STDOUT_PIPE
});
this._proc.init(null);
// Get the stdout pipe and wrap it in a buffered stream
// with some useful helpers
let stdout = new Gio.DataInputStream({
base_stream: this._proc.get_stdout_pipe()
});
// This function will spawn dedicated a thread, reading and buffering
// bytes until it finds a line ending and then invoke onLineRead() in
// in the main thread.
stdout.read_line_async(
GLib.PRIORITY_DEFAULT,
null // Cancellable, if you want it
onLineRead.bind(this)
);
// Check the process completion
this._proc.wait_check_async(null, onProcExited.bind(this));
}
Запись рекурсивного чтения l oop, как это довольно просто, и функция GTask (*_async()
/ *_finish()
) позаботится планирования обратных вызовов в основном l oop для вас. Все операции ввода / вывода выполняются в выделенном потоке, поэтому вся работа, которую вы выполняете при обработке вывода, не блокируется.
Когда вы в конечном итоге отбросите ссылку на this._proc
, вы можете быть уверены, что все каналы и ресурсы очищены должным образом, избегайте висящих файловых дескрипторов, процессов zomb ie и так далее. Если вам нужно выйти из процесса раньше, вы всегда можете позвонить Gio.Subprocess.force_exit()
. Само чтение l oop будет содержать ссылку на упаковщик stdout pipe, так что вы можете просто оставить его, чтобы делать свое дело.