Documentation
Commentary
Short: it unroll "callback hell" to static sequence, also provide
better handling for plist.
Why? Most commonly, you will encounter callbacks in the url.el library.
When you request a page with the (url-retrieve url callback) command,
control returns immediately. Once the page is retrieved, it is passed
as parameters to the callback function. This creates a chain of calls,
which is known as the "callback hell" problem - nested indented bloks.
Async functions don't return "futures/promises". Instead, together
they build chain of calls that executed according to pipeline,
asynchronous.
Other Emacs packages with solution for "callback hell":
- deferred https://github.com/kiwanami/emacs-deferred/tree/master
- promise https://github.com/chuntaro/emacs-promise
- aio https://github.com/skeeto/emacs-aio
What? Static chain of callbacks that unroll at call.
Configuration:
(require 'async1)
Usage:
Define and run pipeline of callbacks with `async1-start' function.
Examples 1. Sequential and parallel steps with default template
(async1-start nil
'((:result "Step 1" :delay 1)
(:parallel
(:result "Parallel A" :delay 2)
(
(:result "Sub-seq a" :delay 1)
(:result "Sub-seq b" :delay 1)
)
(:result "Parallel B" :delay 2))
(:result "Step 3" :delay -1)))
"Final result: {Step 1 -> Sub-seq a -> Sub-seq b, Step 1 -> Parallel B, Step 1 -> Parallel A} -> Step 3"
Each async records-functions wrapped in lambda that call to next record with result.
All lambda functions created as a one lambda and we call it.
You may call own function defined with (data callback) parameters.
You may redefine `async-default-aggregator' for parallel
calls. There may be only one aggregator for now.
:parallel should be at the beginin of list
:aggregator may be anywhere in parallel list
Deep trees should work also.
2. Mixing custom function and parallel steps
(defun custom-async-step (data callback)
"Custom async function that modifies data differently.
CALLBACK is optionall and may be ignored, see `async-create-function'
for refence."
(run-at-time 1.5 nil callback
(concat data " -> Custom Step")))
(async1-start nil
'((:result "Step 1" :delay 1)
(:parallel
custom-async-step
(:result "Parallel B" :delay 1))
(:result "Step 3" :delay 1)))
3. With custom aggregator
(defun custom-aggregator (results)
"Custom aggregator that joins results with ' & '."
(concat "{" (mapconcat 'identity results " & ") "}"))
(async1-start nil
'((:result "Step 1" :delay 1)
(:parallel
(:result "Parallel A" :delay 1)
(:result "Parallel B" :delay 2)
:aggregator #'custom-aggregator)))
Output: "Final result: {Step 1 -> Parallel B & Step 1 -> Parallel A}"
4. Use external data in callback and callback with one argument
(let* ((var "myvar")
(stepcallback)
(callback1 (lambda (data)
(funcall stepcallback (concat data " -> " var))))
(call (lambda (data callback)
(setq stepcallback callback)
(run-at-time 0 nil callback1
(concat data " -> " "Step1"))))
)
(async1-start nil
(list call
call
call
)))
Output: "Final result: -> Step1 -> myvar -> Step1 -> myvar -> Step1 -> myvar"
5. Use mutable lambdas
(let* ((call (lambda (step)
(lambda (data callback)
(run-at-time 0 nil callback
(concat data " -> " "Step" (number-to-string step)))))
))
(async1-start nil
(list (funcall call 0)
(funcall call 1)
(funcall call 2)
(funcall call 3))))
Output: "Final result: -> Step0 -> Step1 -> Step2 -> Step3"
Battlefield example: ehttps://github.com/Anoncheg1/oai/blob/main/oai-prompt.el
Note: (`run-at-time' after-time repeat function &rest args) -
frequently used to imulate function with callback.
Requires
Dependencies
Consumers
Reverse Dependencies
No reverse dependencies recorded.