-
Notifications
You must be signed in to change notification settings - Fork 1
overview
Asynchronous programming using coroutines might mitigate "callback hell", but it can still be troublesome to think about. It can help to compare it to threads.
Each 'a-sync' block (see (a-sync coroutines)) or 'compose-a-sync' block (see (a-sync compose)) is a separate unit of computation, which appears within itself to proceed sequentially even though in fact it executes asynchronously on an event loop. Each such block also appears to execute concurrently with other 'a-sync' or 'compose-a-sync' blocks running on the same event loop. Each 'a-sync' or 'compose-a-sync' block is therefore in some sense analogous to a thread of execution.
If the event loop provided by the (a-sync event-loop) module is in use, the mechanism by which these "pseudo-threads" execute on the event loop is by a call to event-loop-run!. event-loop-run! therefore behaves in a way which is in some sense analogous to a scheduler, although multi-plexing is co-operative and/or i/o event based rather than pre-emptive.
event-loop-run! also performs the equivalent of a join operation on these "pseudo-threads" running on the event loop in question, because by default it returns when there are no further events to be dealt with (that is, when all "pseudo-threads" have completed). So given the following code, the sum of 1 and 1 will only be printed when both the timeout has expired and the calculation has been made, both of which will run as if concurrently (the summing will complete before the timeout expires):
(let ((loop (make-event-loop))
(ret #f))
(a-sync (lambda (await resume)
(await-timeout! await resume loop
100
(lambda () #f))))
(a-sync (lambda (await resume)
(set! ret
(await-task! await resume loop
(lambda ()
(+ 1 1))))))
(event-loop-run! loop)
(display ret)(newline))
"Pseudo-threads" (that is 'a-sync' or 'compose-a-sync' blocks) running on the same event loop can intercommunicate by holding meetings to exchange a datum (see (a-sync meeting). A somewhat similar result can also be achieved using generators (see the await-generator! procedure in the (a-sync event-loop) module).
One interesting feature of these "pseudo-threads" is that where one 'a-sync' or 'compose-a-sync' block is waiting on a read or write watch (including in the await-get*, await-put*, await-connect! or await-accept! procedures), another a-sync block running on the same event loop can cause execution of the waiting block to come to an end by removing the watch using event-loop-remove-read-watch! or event-loop-remove-write-watch!, as the case may be (and another native OS thread can do this by posting an event to the event loop which does the same).
Apart from these "pseudo-threads", true parallelism is possible using native OS threads, with the await-task-in-thread!, await-task-in-event-loop!, await-task-in-thread-pool!, await-generator-in-thread!, await-generator-in-event-loop! and await-generator-in-thread-pool! procedures.
Modules:
- (a-sync coroutines)
- (a-sync event-loop)
- (a-sync await-ports)
- (a-sync thread-pool)
- (a-sync monotonic-time)
- (a-sync gnome-glib)
- (a-sync compose)
- (a-sync meeting)
- (a-sync g-golf)
- (a-sync guile-gi)
Other: