Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
stylua
nvim-test
luals

doc.json
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,11 @@ luals-check: luals nvim-test
--logpath=. \
--configpath=../.luarc.json \
--check=lua

.PHONY: doc
doc:
emmylua_doc_cli \
--input lua \
--output . \
--format=json
./docgen.lua doc.json doc/lua-async.txt
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ First we turn this into an async function using `wrap`:

local async = require('async')

local run_job_a = async.awrap(3, run_job)
local run_job_a = async.wrap(3, run_job)
```

Now we need to create a top level function to initialize the async context. To do this we can use `void` or `sync`.
Expand All @@ -62,7 +62,7 @@ Note: the main difference between `void` and `sync` is that `sync` functions can
For this example we will use `void`:

```lua
local code = async.arun(function()
local code = async.run(function()
local code1 = run_job_a('echo', {'foo'})
if code1 ~= 0 then
return
Expand All @@ -80,7 +80,7 @@ end):wait()
We can now call `run_job_a` in linear imperative fashion without needing to define callbacks.
The arguments provided to the callback in the original function are simply returned by the async version.

Additionally because `run_job_a` returns an object with a `close()` method (as a `uv_process_t`), `asrync.arun` will automatically `close` the handle if either the task completes or is interuppted,
Additionally because `run_job_a` returns an object with a `close()` method (as a `uv_process_t`), `asrync.run` will automatically `close` the handle if either the task completes or is interuppted,

## Kinds of async functions

Expand Down Expand Up @@ -121,7 +121,7 @@ Cons:
## Async function nesting

Unlike Python or Javascript, in async.nvim not all functions need to be defined/created as such.
Instead async functions can be regular functions but they must be executed in an async context (via `async.arun()`).
Instead async functions can be regular functions but they must be executed in an async context (via `async.run()`).
If a function is created with `async.async()` then when it is called it execute in a new async context and therefore will be non-blocking.

```lua
Expand All @@ -141,8 +141,8 @@ foo(a, b)
foo(a, b):wait()
-- sync, blocking

-- use async.arun to create an async context
async.arun(function()
-- use async.run to create an async context
async.run(function()

bar(a, b)
-- async, blocking
Expand Down
200 changes: 155 additions & 45 deletions async.md
Original file line number Diff line number Diff line change
@@ -1,97 +1,207 @@
# async

This library provides a set of utilities for asynchronous programming in Lua.
It includes support for tasks, events, queues, semaphores, and more, enabling developers to write non-blocking, asynchronous code.

Small async library for Neovim plugins

## Functions
## Library

### Core functions

### `async(func)`
#### `async.async(fun)`
Create an asynchronous function.

Create an async function.
- **Parameters**:
- `fun` (function): The function to wrap as asynchronous.
- **Returns**: `async.TaskFun`

#### Parameters:
---

* `func` (`function`)
#### `async.await(...)`
Await the result of a task, callback function, or task function.

#### Returns:
- **Parameters**:
- `...` (any): Task, callback function, or task function to await.
- **Returns**: Results of the awaited operation.

---

#### `async.run(func, ...)`
Run a function in an asynchronous context.

* `vim.async.Task`: handle to running async function.
- **Parameters**:
- `func` (function): The function to run asynchronously.
- `...` (any): Arguments to pass to the function.
- **Returns**: `async.Task`

---

### `await(argc, func, ...)`
#### `async.wrap(argc, func)`
Create an asynchronous function from a callback-style function.

Must be run in an async context.

Asynchronously wait on a callback style function.
- **Parameters**:
- `argc` (integer): Number of arguments before the callback.
- `func` (function): The callback-style function.
- **Returns**: `async function`

#### Parameters:
---

### `async.iter(tasks)`

* `argc` (`integer`): Position of the callback argument in `func`.
* `func` (`function`):
* `...` (`any[]`): arguments for `func`
Must be run in an async context.

#### Returns:

Return values of `func`.
Iterator function that yields the results of each task.

---

### `arun(func)`
### `async.join(tasks)`

#### Parameters:
Run a collection of async functions (`thunks`) concurrently and return when
all have finished.

* `func` (`function`):
---

#### Returns:
### `async.schedule()`

An async function that when called will yield to the Neovim scheduler to be
able to call the API.

* `vim.async.Task`: handle to running async function.
Must be run in an async context.

---

### `awrap(argc, func)`
### Task Management

Wraps callback style function so it asynchronously waits
in an async context.
#### `async.Task`

#### Parameters:
##### `Task:await(callback)`
Await the completion of a task.

* `argc` (`integer`): The number of arguments of func. Must be included.
* `func` (`function`): A callback style function to be converted. The last argument must be the callback.
- **Parameters**:
- `callback` (function): Callback to invoke when the task completes.

#### Returns:
##### `Task:wait(timeout?)`
Synchronously wait for a task to finish.

Wrapped function of `func`.
- **Parameters**:
- `timeout` (integer, optional): Timeout in milliseconds.
- **Returns**: Results of the task.

Must be run in an async context.
##### `Task:close(callback?)`
Close the task and all its children.

---
- **Parameters**:
- `callback` (function, optional): Callback to invoke after closing.

### `iter(tasks)`
##### `Task:traceback(msg?)`
Get the traceback of a task.

Must be run in an async context.
- **Parameters**:
- `msg` (string, optional): Additional message to include in the traceback.
- **Returns**: `string`

#### Returns:

Iterator function that yields the results of each task.

---
### Utilities

### `join(n, thunks, interrupt_check)`
#### `async.event()`
Create a new event.

Run a collection of async functions (`thunks`) concurrently and return when
all have finished.
- **Returns**: `async.Event`

#### `async.queue(max_size?)`
Create a new FIFO queue with async support.

#### Parameters:
- **Parameters**:
- `max_size` (integer, optional): Maximum number of items in the queue.
- **Returns**: `async.Queue`

* `n` (`integer`): Max number of thunks to run concurrently
* `thunks` (`function[]`):
* `interrupt_check` (`function`): Function to abort thunks between calls
#### `async.semaphore(permits)`
Create an async semaphore.

- **Parameters**:
- `permits` (integer): Number of permits for the semaphore.
- **Returns**: `async.Semaphore`

---

### `schedule()`
## Examples

An async function that when called will yield to the Neovim scheduler to be
able to call the API.
### Running an Asynchronous Task

```lua
local async = require('async')

local task = async.run(function()
print("Task started")
async.await(1, vim.schedule)
print("Task finished")
end)

task:wait()
```

### Using an Event

```lua
local async = require('async')

local event = async.event()

async.run(function()
print("Waiting for event...")
event:wait()
print("Event triggered!")
end)

vim.schedule(function()
event:set()
end)
```

### Using a Queue

```lua
local async = require('async')

local queue = async.queue(5)

async.run(function()
for i = 1, 5 do
queue:put(i)
end
queue:put(nil) -- Signal end
end)

async.run(function()
while true do
local value = queue:get()
if value == nil then break end
print("Got value:", value)
end
end)
```

### Using a Semaphore

```lua
local async = require('async')

local semaphore = async.semaphore(2)

for i = 1, 5 do
async.run(function()
semaphore:with(function()
print("Task", i, "started")
async.await(1, vim.schedule)
print("Task", i, "finished")
end)
end)
end
```

Must be run in an async context.
Loading