Tuesday, July 03, 2018

Using Emacs Threads To Execute Commands Asynchronously

1 Executive Summary

Emacs 26 has threads for executing functions asynchronously. Emacs
commands that call an external process and wait for that process to
finish make a good candidate for asynchronous execution — e.g.,smtpmail-send-it for sending mail. The arrival of threads provides
an interesting option for running such commands asynchronously.

2 First Attempt — Custom Async gnus Command

I initially wrote a custom command for launching gnus asynchronously
— it was a one-line function that ran the following:

(make-thread #'gnus)

The above worked well — except when command gnus needed user input
— so I just had to be thoughtful about when I called it. But a few
weeks later, I wanted the equivalent for function smtpmail-send-it
for sending mail. I almost wrote myself one more command before
stepping back to create a more generic solution.

3 One Command To Thread Them All

I have now defined command emacspeak-wizards-execute-asynchronously
bound to C-' a.
Note that this command, though part of module emacspeak-wizards, has
no emacspeak dependencies.

With this command bound to C-' a, I can now get rid of my customgnus-async command and its associated key-binding. I already have
command gnus bound to C-; g, so I can just press C-' a C-; g to
fetch news/mail asynchronously.

Similarly, when sending mail using smtpmail I can press C-' a C-c
C-c in the *mail* buffer to send mail without Emacs blocking.

4 Final Caveats

Like other asynchronous solutions (see package async for instance)
one needs to make sure that the command being executed asynchronously
will not require user input. In the case of package async, the
asynchronous Emacs will block waiting for input; in the case ofmake-thread, Emacs enters a blocking loop with the minibuffer
continuously displaying

No catch for ...

The only way to come out is to kill Emacs — so make sure to use
command emacspeak-wizards-execute-asynchronously only when you're
sure that the command being run asynchronously will not require user
input.