I use the async to call async function in elisp.
First of all I test the readme code in github and it works well.
Then I write a test code :
(defun async-function ()
(async-start
;; What to do in the child process
(lambda ()
(shell-command-to-string "~/test.sh"))
;; What to do when it finishes
(lambda (result)
(message "Async process done, result is: %s" result)))
And the test.sh code is very simple:
#! /usr/bin/env sh
sleep 2
echo "shell finish"
It works , But it failed when I change the lisp code like that :
;;;###autoload
(defun test ()
(shell-command-to-string "~/test.sh"))
(defun async-function ()
(async-start
;; What to do in the child process
(lambda ()
(test))
;; What to do when it finishes
(lambda (result)
(message "Async process done, result is: %s" result)))
The result is:
error in process sentinel: Symbol's function definition is void: test
I use the autoload
function to load the function file but the result tell me that the file can not found.
I have no idea what happen about that and how to fix.
async.el works by spawning another Emacs process in which to evaluate the expressions. In this case you define the test function in your main Emacs process but the async Emacs process has no access to the functions and variables from your main process. If you want to specify functions for your async calls to use, put them in a file and load that file in your async function. But remember that variables and the like will not transfer over.
Here is a an example, this is in one file:
;; ~/.emacs.d/my-functions.el
(defun test ()
(sit-for 2)
"Hello, World!.")
And this is somewhere else:
;; somewhere else
(async-start
(lambda ()
(load-file "~/.emacs.d/my-functions.el")
(test))
(lambda (result)
(message "result: %s" result))) ;; will message hello world
Regarding sharing variables, this is not supported by async. What you CANNOT do is start up an async function that continually modifies a variable and expect to have access to it on the main emacs process, or even just pass the variable into the async lambda, because the symbol won't be evaluated until it's on the new process where it won't exist.
We can make use of Lisp's backquote syntax to pass our variables into our async function by value. Below is a very hacky way of using a local variable and function in an async function.
;; We will declare a local variable `myvar'
(setq myvar "Bob")
;; Here is a simple function, notice that it does not
;; refer to other non standard functions or other local variables
(defun hello (name)
(format "Hello, %s!" name))
(defun my-async-function ()
(async-start
;; notice the backquote!
`(lambda ()
;; in our async lambda we dont use the local `myvar' variable,
;; instead we are replacing it with the current local value of `myvar'
(set 'myvar ,myvar)
;; we can also do this by actually obtaining the lambda expression
;; behind `hello' and putting that inside our lambda
(fset 'hello ,(symbol-function 'hello))
;; then we wait!
(sit-for 1)
;; now we are modifiying the async copy of `myvar'
(setq myvar (concat myvar " Johnson"))
;; here we want the result of the async lambda to be a call to our
;; `hello' function, but we also want to update our local version
;; of myvar with its value in th async process, so we will return
;; a list of both values which we can handle in our callback
(list myvar (hello myvar)))
(lambda (result)
;; once we get the results we'll update our local version of `myvar'
;; with the value returned by the async function
(setq myvar (first result))
;; then we can print out the message we recieved from the output
;; of our async `hello' call.
(message "The new value myvar is: %s\nThe result of the function was: %s"
myvar
(second result)))))
;; executed top down, the async callback will message:
;;The new value myvar is: Bob Johnson
;;The result of the function was: Hello, Bob Johnson!
I have abstracted out the concept of replacing variables and functions with their immediate value with a macro: value-bound-lambda, you can get the macro and see an example here:
value-bound-lambda example
Just want to share a technique I use to run a custom function using async
and not wanting to hard-code the file path. Assume I want to run the function my-custom-function
that is in the file my-custom.el
(i.e. "~/emacs.d/my-custom/my-custom.el"). So the symbol-file
will return the filename where the function is defined and file-name-directory
returns it's parent directory.
(let ((script-filename (file-name-directory (symbol-file 'my-custom-function))))
(async-start
`(lambda()
(add-to-list 'load-path ,script-filename)
(require 'my-custom)
(my-custom-function)
....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With