Pregunta ¿Cuál es la razón de que async.el diga "la definición de la función del símbolo es nula" en Emacs Lisp


Yo uso el async para llamar a la función async en elisp.

En primer lugar, pruebo el código readme en github y funciona bien.

Luego escribo un código de prueba:

(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)))

Y el código test.sh es muy simple:

#! /usr/bin/env sh
sleep 2
echo "shell finish"

Funciona, pero falló cuando cambio el código lisp de esa manera:

;;;###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)))

El resultado es:

 error in process sentinel: Symbol's function definition is void: test

Yo uso el autoload función para cargar el archivo de función, pero el resultado me dice que no se puede encontrar el archivo.

No tengo idea de qué pasa con eso y cómo solucionarlo.


5
2018-04-03 15:40


origen


Respuestas:


async.el funciona generando otro proceso de Emacs en el que evaluar las expresiones. En este caso, define la función de prueba en su proceso principal de Emacs, pero el proceso asíncrono de Emacs no tiene acceso a las funciones y variables de su proceso principal. Si desea especificar funciones para sus llamadas asíncronas, colóquelas en un archivo y cargue ese archivo en su función asíncrona. Pero recuerda que las variables y similares no se transferirán.

Aquí hay un ejemplo, esto está en un archivo:

;; ~/.emacs.d/my-functions.el
(defun test ()
  (sit-for 2)
  "Hello, World!.")

Y esto es en otro lugar:

;; somewhere else
(async-start
 (lambda ()
   (load-file "~/.emacs.d/my-functions.el")
   (test))
 (lambda (result)
   (message "result: %s" result))) ;; will message hello world

Con respecto a compartir variables, esto no es compatible con async. Lo que NO PUEDE hacer es iniciar una función asíncrona que modifique continuamente una variable y esperar tener acceso a ella en el proceso principal de emacs, o simplemente pasar la variable a la async lambda, porque el símbolo no se evaluará hasta que esté encendido El nuevo proceso donde no existirá.

Podemos hacer uso de la sintaxis backquote de Lisp para pasar nuestras variables a nuestra función asíncrona por valor. A continuación se muestra una forma muy intrincada de usar una variable y función local en una función asíncrona.

;; 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!

He resumido el concepto de reemplazar variables y funciones con su valor inmediato con una macro: value-bound-lambda, puede obtener la macro y ver un ejemplo aquí:

value-bound-lambda example


14
2018-04-03 16:14



Solo quiero compartir una técnica que uso para ejecutar una función personalizada usando async y no querer codificar la ruta del archivo. Suponer que quiero ejecutar la función my-custom-function eso está en el archivo my-custom.el (es decir, "~ / emacs.d / my-custom / my-custom.el"). Entonces el symbol-file devolverá el nombre del archivo donde se define la función y file-name-directory devuelve su directorio principal.

(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)
    ....

1
2017-11-22 09:18