Package sprout provides types and utilities for implementing client and server programs that speak the Sprout Protocol. The Sprout Protocol is specified here: https://man.sr.ht/~whereswaldon/arborchat/specifications/sprout.md NOTE: this package requires using a fork of golang.org/x/crypto, and you must therefore include the following in your `go.mod`: This package exports several important types. The Conn type wraps a connection-oriented transport (usually a TCP connection) and provides methods for sending sprout messages and reading sprout messages off of the connection. It has a number of exported fields which are functions that should handle incoming messages. These must be set by the user, and their behavior should conform to the Sprout specification. If using a Conn directly, be sure to invoke the ReadMessage() method properly to ensure that you receive repies. The Worker type wraps a Conn and provides automatic implementations of both the handler functions for each sprout message and the processing loop that will read new messages and dispatch their handlers. You can send messages on a worker by calling Conn methods via struct embedding. It has an exported embedded Conn. The Conn type has both synchronous and asynchronous methods for sending messages. The synchronous ones block until they recieve a response or their timeout channel emits a value. Details on how to use these methods follow. Note: The Send* methods The non-Async methods block until the get a response or until their timeout is reached. There are several cases in which will return an error: - There is a network problem sending the message or receiving the response - There is a problem creating the outbound message or parsing the inbound response - The status message received in response is not sprout.StatusOk. In this case, the error will be of type sprout.Status The recommended way to invoke synchronous Send*() methods is with a time.Ticker as the input channel, like so: Note: The Send*Async methods The Async versions of each send operation provide more granular control over blocking behavior. They return a chan interface{}, but will never send anything other than a sprout.Status or sprout.Response over that channel. It is safe to assume that the value will be one of those two. The Async versions also return a handle for the request called a MessageID. This can be used to cancel the request in the event that it doesn't have a response or the response no longer matters. This can be done manually using the Cancel() method on the Conn type. The synchronous version of each send method handles this for you, but it must be done manually with the async variant. An example of the appropriate use of an async method:
Package async is a library for asynchronous programming. Since Go has already done a great job in bringing green/virtual threads into life, this library only implements a single-threaded Executor type, which some refer to as an async runtime. One can create as many executors as they like. While Go excels at forking, async, on the other hand, excels at joining. Wanted to execute pieces of code from various goroutines in a single-threaded way? An Executor is designed to be able to run tasks spawned in various goroutines sequentially. This comes in handy when one wants to do a series of operations on a single thread, for example, to read or update states that are not safe for concurrent access, to write data to the console, to update one's user interfaces, etc. No backpressure alert. Task spawning is designed not to block. If spawning outruns execution, an executor could easily consume a lot of memory over time. To mitigate, one could introduce a semaphore per hot spot. A Task can be reactive. A task is spawned with a Coroutine to take care of it. In this user-provided function, one can return a specific Result to tell a coroutine to watch and await some events (e.g. Signal, State and Memo, etc.), and the coroutine can just re-run the task whenever any of these events notifies. This is useful when one wants to do something repeatedly. It works like a loop. To exit this loop, just return a Result that ends the coroutine from within the task function. Simple. A Coroutine can also transit from one Task to another, just like a state machine can transit from one state to another. This is done by returning another specific Result from within a task function. A coroutine can transit from one task to another until a task ends it. With the ability to transit, async is able to provide more advanced control structures, like Block, Loop and Func, to ease the process of writing async code. The experience now feels similar to that of writing sync code. It's not recommended to have channel operations in an async Task for a Coroutine to do, since they tend to block. For an Executor, if one coroutine blocks, no other coroutines can run. So instead of passing data around, one would just handle data in place. One of the advantages of passing data over channels is to be able to reduce allocation. Unfortunately, async tasks always escape to heap. Any variable they captured also escapes to heap. One should always stay alert and take measures in hot spot, like repeatedly using a same task. This example demonstrates how to spawn tasks with different paths. The lower path, the higher priority. This example creates a task with path "aa" for additional computations and another task with path "zz" for printing results. The former runs before the latter because "aa" < "zz". This example demonstrates how to add a function call before a task re-runs, or after a task ends. This example demonstrates how a task can conditionally depend on a state. This example demonstrates how a memo can conditionally depend on a state. This example demonstrates how to end a task. It creates a task that prints the value of a state whenever it changes. The task only prints 0, 1, 2 and 3 because it is ended after 3. This example demonstrates how to use memos to memoize cheap computations. Memos are evaluated lazily. They take effect only when they are acquired. This example demonstrates how to set up an autorun function to run an executor in a goroutine automatically whenever a coroutine is spawned or resumed. This example demonstrates how a coroutine can transit from one task to another.
Package async is a package to provide simplistic asynchronous routines for the masses.
Package async is a package to provide simplistic asynchronous routines for the masses.