[Library] Lua Asyncio |
Tocutoeltuco « Censeur » 1556312700000
| 6 | ||
Today I've written a library that works with transformice lua and should work with any other lua version. It is based in the python asyncio library but written in lua. It doesn't have all the features that python asyncio provides but it is useful and it is working. First of all: What is asyncio? Asyncio is a library to write concurrent and asynchronous code. It allows you to run many functions at once without getting a "runtime exceeded" error. How does it work? Asyncio works with a loop (in the python version it is called event loop), which runs a queue of tasks. Those tasks are coroutined (asynchronous) functions, which yield and call other coroutined functions, and in the process, the loop runs other tasks of the queue. This means, that within an asynchronous function you can have many other asynchronous calls, and when the code reaches an asynchronous call, it pauses the current task, moves it to the end of the queue and runs the next task. How do I use it? It is really easy to use: You need to copy the library code at the start of your script. You call the Loop() function with no arguments and save the result into a variable, which you can easily configure: The variable is a table, where you can edit some attributes: loop.max_runtime: This is the attribute that tells the library what is the maximum runtime for a loop iteration. I suggest you to set it to 30 or 20. loop.iteration_cooldown: This is the attribute that tells the library what is the cooldown between an iteration and another. The value of this attribute can be different depending on which tasks you're running and what is the max_runtime you're gonna use. I suggest you to set it to 4000 or a greater value loop.accept_faster: This is the attribute that tells the library if it can ignore the cooldown when the runtime is not on its limits yet. Example, if the loop.run_tasks is called once and it takes less than loop.max_runtime, if it is called once again before the loop.iteration_cooldown completes, and this value is true, the function will accept another iteration. Not recommended when you're spamming heavy tasks, but recommended if you're not. loop.error_handler: This is the function that handles the errors that happen in the tasks. It is listed as a new task when there is an error. It receives only one argument which is the error message. Normally it just prints the error message. The errors that happen in this task don't re-call error_handler, they just propagate outside the loop.run_tasks function You can use some variables and functions from the loop too: loop.current_task: This is the last task that has been called from the loop. If you're using it inside a task, it will return the task object from where you've accessed this variable loop.iteration_no: The iteration that the loop is actually running loop.add_task: This is a function that adds a task to the queue. Receives only one argument which must be a task object, otherwise your code will just crash loop.sleep: This is a function that pauses the running task and moves it to the end of the queue after X time. Receives only one optional argument, which is the time that it must sleep for. If it is not set, the task will be added at the end of the queue in the next loop iteration, otherwise, it will wait the specified time and then add the task at the end of the queue loop.run_tasks: This is the function that makes an iteration for the loop. It receives only one optional argument which is where it must start counting the runtime from. If it is not set, it will make a call to os.time() and start counting from that. If the difference between the actual time and the start one is greater or equal to max_runtime, then it will end the function To create a Task object, you just call the Task function with the function you need to turn into a task and with the arguments you're gonna send to the function. Example: Task(print, "heyy") To append the task object to the end of the queue, it depends on where you're calling and what you're trying to do. If you're trying to add the task to the end of the queue from a normal function (which is not a task) you must do loop.add_task(task). That function just adds the function to the end of the queue but doesn't pause the current running task/function. If you're inside a task you can do that or you can just call coroutine.yield(task). It will return two or more values: true/false telling whether the task ran successfully (if it is false, it called loop.error_handler right before this task) and the function returned values. If you yield a value that is not a task from a task, it will just pause the running task and won't resume it never, unless you add it back to the queue somehow From a task you can use many values: task.coro: It is the task coroutine task.runtime: The runtime that the task is running for. Doesn't count the time that every other task use, but it counts the tasks that are called with coroutine.yield from this task task.iteration: The iteration where the task is being run. It is 0 if it didn't run yet. It might be changing while the task is running. When it is done, this is the last iteration that handled the task. Example: If it runs in the iterations 3, 6, 7, when it is done, this value will be 7 task.cancelled: Normally it is false, but if you set it to true, if it is running, it will wait until it ends running and when the loop reaches that task again it will just remove it from the queue task.done: Whether if the task has finished (it counts if the task has thrown an error too) task.result: A table with two or more values: the ones that you get from yielding the task. It is nil if the task.done is false (if you manually set it to true, it won't make task.result turn into a table magically) task.after_done: The task that yielded this task. It is nil if it was added by loop.add_task. Don't change this value or you'll break the library task.get_result: Normalizes the task result, so if it threw an error, calling this will just throw that error. If it finished normally it will just return the values. task.real_runtime: Returns the runtime that the task is using until now. Difference from this and task.runtime is that the value of that variable is the runtime that the task used until the last yield. As said before, you can get the current working task by accessing loop.current_task. If it is called outside of a running task, it will return the last executed task After that, you add a call to loop.run_tasks in your eventLoop and asyncio will start working! You can call the loop.run_tasks function at the end of any event. It makes a difference (improves it) when loop.accept_faster is set to true P.S: if you need to run some code before the loop.run_tasks call, you'll need to save the time when the eventLoop (or any other event where you're calling this from) started executing and then send that as an argument to loop.run_tasks. Here is an example: Code Lua 1 2 3 4 5 function eventLoop() Here is the library And here are 3 examples: Heavy task spamming Soft task spamming Another example |
1 | ||
Thank you so much! ♥ |
Bolodefchoco « Sénateur » 1556383920000
| 1 | ||
Looks terrific! ;o |
0 | ||
Seems very useful! Can you create a minified version also? Dernière modification le 1566920100000 |
0 | ||
Massi a dit : |