Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Learning TaskChain Terminology
A Task Chain could also be called a Task Pipeline. You have a series of tasks that are executed sequentially. The tasks are "chained" together by passing their return values to the next task. The API is also a fluent interface (Method Chaining), so that is where the name TaskChain came from!
When the word Sync is used, it represents the desire to run on the application/games "Main Thread" (usually required for API safety)
When the word Async is used, it represents the desire to run off of the application/games "Main Thread", inside of a thread pool. Async Tasks may run in parallel.
When the word Current is used, it represents that the task does not care which thread it runs on, and it should use whatever the current chain happens to be (do not perform any switch from the previous tasks thread)
Each chain has a HashMap associated to it that you can use to store data. Since only one task can ever be executing in the same chain, the map is functionally thread safe.
You may use this map when you need to store multiple values to be later accessed in another task.
Once all Tasks have been added to the chain/pipeline, executing it will start execution of the tasks. Once a chain has been executed, no more tasks may be added to it.
Aborting / Abort If Null
If an error occurs, you may wish to stop executing the chain. You may call TaskChain.abort() to abort any chain (An exception will be thrown that TaskChain will catch)
You may use the API .abortIfNull to test if the previous task returned a null value, and abort the chain if so. You may also optionally pass in "Actions" to perform when the null abortion has been triggered. As an example, send the player a message that their action has failed and why.
A factory is an object that binds your code (Your plugin or game) to a game implementation (Such as a Minecraft Server). Many game API's require identification when using their API's such as schedulers. Your game implementation may require passing in the object needed to be passed to other API's (In Minecraft servers: the plugin instance)
The Factory is also responsible for creating an asynchronous thread pool to handle dispatching async tasks, which must be shut down when your game is shutting down to ensure completions.
So any chain created out of a TaskChain factory automatically has all the information it needs to execute (The Game Implementation and Thread Pool)
Then you may use static utility methods to access your factory to create chains.
This is the basic "Middle Task", that takes an input argument (A = argument, R = return), and returns a value. These would never be used as your first or last task in the chain as you will not have anything to input to it on the first task, nor would the return value be used as a last task.
The argument type will be determined by the type parameter of the chain at its state. For example, of the previous task returned an Integer, you will currently be at a TaskChain state in the chain. Then when you add a Task<R, A> to the chain, A is understood to be an Integer due to the part of the chain.
This is a task that does not take any input. It is usually going to be used as your first task in the chain, or a task that may be using data out of the Task Data Map instead. This Task will return data
Like a first task, but the opposite, in that it will not return a value, and only takes an argument
This task takes no input nor does it return any value. Useful when you are simply just trying to ensure thread execution context for a body of code, or need to ensure 2 async tasks do not execute at the same time using a shared chain.
Each of the 3 Task Types have an AsyncExecuting form. This part of TaskChain is a bit confusing, as the phrase "Async" is being used in 2 ways, but in this case, it's actually the more appropriate use.
A function that uses return statements to return its response is a "synchronous" function (with the exception of returning futures/promises, which then that function hasn't returned its actual response yet)
An asynchronous functions will take a function as one of its arguments, where the "response" is meant to be passed to that function. Or it may return a Promise/Future, which says "I will pass my response to this future later"
When function s behave like this, they are asynchronous, as you are not guaranteed when that function is considered "Done" and you have its response.
AsyncExecuting Tasks are exactly this. When TaskChain calls one of these tasks, it does not move to the next task upon return of the task. Instead, it will wait until the next parameter on the task has been executed.
An async executing task may pass that next parameter off to other code, to other threads, which may not be finished until 3 minutes later. Util that next.run() method is called, the chain is paused!
This kind of behavior is useful when you are calling other asynchronous methods and do not want to proceed until that other call has finished.
Since .async() was already used in the API, the API uses "Callback" such as .asyncCallback() to declare that the task will be asynchronous in its execution. The .async and .sync part of the API declares the thread context the initial part of the task should run on, and callback defines its behavior.
Since the chain will be paused, you are free to switch threads your self before calling next.run(). It will be thread safe.