GjsPipe provides utilities to safely manage your Gio.Subprocess.
The library provides a Pipe class for easily and safely creating
processes in your gjs
application and consuming the process
output line by line.
// define a process to run a script in your system
let p = new Pipe('bash', '-c', 'while sleep 1; do echo "looping"; done')
// start the process and start reading output line by line
let cancel = p.start(line => print(line))
// if required, stop the pipe using the cancel function
cancel()
// That's all!
WARNING: Make sure to call cancel
before exiting your gjs
app.
An unclean exit of your gjs
app can create orphaned processes in your system.
Unfortunately gjs
does not ensure that all instances of Gio.Subprocess
are killed automatically on program exit.
Here is an example how to turn a system program on and off and consume its output.
const script = 'my-command'
const p = new Pipe('bash', '-c', script)
let cancel = null
function onResult(line) {
print(line)
}
function onExit(ok) {
if (ok) log(`pipe ${script} stopped`)
else logError(new Error(`pipe ${script} failed, see logs for details`))
}
function onError(err) {
logError(err)
if (cancel) {
log(`pipe ${script} had errors, stopping pipe...`)
cancel()
}
}
function startPipe() {
return p.start(onResult, onExit, onError)
}
function toggle() {
if (cancel) { cancel(); cancel = null }
else { cancel = startPipe() }
}
toggleBtn.connect('toggled', (btn) => toggle())
The onExit
and onError
callbacks are optional.
- Without
onError
any errors will be logged vialogError
. - Without
onExit
the pipe may exit and fail silently.
In addition to two simple asyncTimeout
and makeAsync
functions for running
and awaiting any function asynchronously, this library also provides a
glibAsync
function to await
async Glib start and finish calls.
Async Gio and
Glib functions
usually consist of a <func>_async
and a <func>_finish
for calling and handling
async IO. They do not return a Promise
and thus cannot be awaited.
GjsPipe provides glibAsync
to mitigate this.
Instead of callback-based execution, where errors may get lost in async nirvana:
const ctx = new Gio.Cancellable()
try {
proc.wait_check_async(ctx, (_, res) => {
try {
const ok = proc.wait_check_finish(res)
} catch (e) {
// async errors must be handled in the async handler functions
// and somehow be exposed the start-level using another callback
handleAsyncError(e)
}
}
} catch (e) {
// only "start" errors can be catched
}
You can now use async
and await
and have errors thrown to where you started the execution:
try {
const ctx = new Gio.Cancellable()
const ok = await glibAsync(
(finish) => proc.wait_check_async(ctx, finish), // GLib start logic
(_, res) => proc.wait_check_finish(res), // GLib finish logic
)
} catch (e) {
// all errors can handled at the start-level of the async function
}
MIT