In gtk-rs master (not yet released) the GTK event loop can process standard futures and channels of futures. There is a for_each function on channels of futures that does asynchronous processing of received futures on the channel.

let context = glib::MainContext::ref_thread_default();
context.spawn_local({
    let c_w = control_window.clone();
    message_channel.for_each(move |message| {
        match message {
            Message::FrontendAppeared { fei } => add_frontend(&c_w, fei.clone()),
            Message::FrontendDisappeared { fei } => remove_frontend(&c_w, fei.clone()),
        }
        Ok(())
    }).map(|_| ())
});

This way of working relies on having a ready made futures, executors, and channels system with a single threaded task pool system. D doesn't seem to have one, though I am sure std.parallelism and vibe.d both have what is needed within them. Of course Rust has Tokio (equivalent of vibe.d) and Rayon (equivalent of std.parallelism). Tokio appears based on Rust futures, but Rayon appears not to use them. I can sort of understand this, Tokio is I/O bound and so can use the I/O bound futures, whereas Rayon needs something much more lightweight with better work-stealing.

I am not sure making GtkD rely on vibe.d is the right thing to do, and I am not sure the vibe.d people would be up for pulling out their futures stuff as a separate dependency. Creating something just for GtkD doesn't seem cost effective.

I suspect I'll just go with glib.Timeout as a way of mocking up something analogous. I think Timeout rather than Idle, as it should use less CPU resources.