Skip to content

Change assumption in the wait function #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
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
50 changes: 47 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# async.nvim
Small async library for Neovim plugins

[API documentation](async.md)

## Example

Take the current function that uses a callback style function to run a system process.
Expand All @@ -19,8 +21,8 @@ end

If we want to emulate something like:

```lua
echo foo && echo bar && echo baz
```bash
echo 'foo' && echo 'bar' && echo 'baz'
```

Would need to be implemented as:
Expand Down Expand Up @@ -60,7 +62,7 @@ local run_job_a = a.wrap(run_job, 3)

Now we need to create a top level function to initialize the async context. To do this we can use `void` or `sync`.

Note: the main difference between `void` and `sync` is that `sync` functions can be called with a callback (like the `run_job` in a non-async context, however the user must provide the number of agurments.
Note: the main difference between `void` and `sync` is that `sync` functions can be called with a callback, like the original `run_job` in a non-async context, however the user must provide the number of arguments.

For this example we will use `void`:

Expand All @@ -84,3 +86,45 @@ main()

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.

## The `async_t` handle

This library supports cancelling async functions that are currently running. This is done via the `async_t` handle interface.
The handle must provide the methods `cancel()` and `is_cancelled()`, and the purpose of these is to allow the cancelled async function to run any cleanup and free any resources it has created.

### Example use with `vim.loop.spawn`:

Typically applications to `vim.loop.spawn` make use of `stdio` pipes for communicating. This involves creating `uv_pipe_t` objects.
If a job is cancelled then these objects must be closed.

```lua
local function run_job = async.wrap(function(cmd, args, callback)
local stdout = vim.loop.new_pipe(false)

local raw_handle
raw_handle = vim.loop.spawn(cmd, { args = args, stdio = { nil, stdout }},
function(code)
stdout:close()
raw_handle:close()
callback(code)
end
)

local handle = {}

handle.is_cancelled = function(_)
return raw_handle.is_closing()
end

handle.cancel = function(_, cb)
raw_handle:close(function()
stdout:close(cb)
end)
end

return handle
end)
```

So even if `run_job` is called in a deep function stack, calling `cancel()` on any parent async function will allow the job to be cancelled safely.

57 changes: 52 additions & 5 deletions async.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,43 @@ Small async library for Neovim plugins

## Functions

### `sync(func, argc)`
### `running()`

Returns whether the current execution context is async.


#### Returns

`boolean?`

---
### `run(func, callback, ...)`

Run a function in an async context.

#### Parameters:

* `func` (`function`):
* `callback` (`function`):
* `...` (`any`): Arguments for func

#### Returns

`async_t`: Handle

---
### `wait(argc, func, ...)`

Wait on a callback style function

#### Parameters:

* `argc` (`integer?`): The number of arguments of func.
* `func` (`function`): callback style function to execute
* `...` (`any`): Arguments for func

---
### `create(func, argc, strict)`

Use this to create a function which executes in an async context but
called from a non-async context. Inherently this cannot return anything
Expand All @@ -15,19 +51,25 @@ Use this to create a function which executes in an async context but

* `func` (`function`):
* `argc` (`number`): The number of arguments of func. Defaults to 0
* `strict` (`boolean`): Error when called in non-async context

#### Returns

`function(...):async_t`

---
### `void(func)`
### `void(func, strict)`

Create a function which executes in an async context but
called from a non-async context.

#### Parameters:

* `func` (`function`):
* `strict` (`boolean`): Error when called in non-async context

---
### `wrap(func, argc, protected)`
### `wrap(func, argc, protected, strict)`

Creates an async function with a callback style function.

Expand All @@ -36,18 +78,23 @@ Creates an async function with a callback style function.
* `func` (`function`): A callback style function to be converted. The last argument must be the callback.
* `argc` (`integer`): The number of arguments of func. Must be included.
* `protected` (`boolean`): call the function in protected mode (like pcall)
* `strict` (`boolean`): Error when called in non-async context

#### Returns

`function`: Returns an async function

---
### `join(n, interrupt_check, thunks)`
### `join(thunks, n, interrupt_check)`

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

#### Parameters:

* `thunks` (`function[]`):
* `n` (`integer`): Max number of thunks to run concurrently
* `interrupt_check` (`function`): Function to abort thunks between calls
* `thunks` (`function[]`):

---
### `curry(fn, ...)`
Expand Down
19 changes: 19 additions & 0 deletions ldoc.ltp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,25 @@ $(lev3) $(subnames):
> end
> end -- for
> end -- if params
> if item.retgroups then

$(lev3) Returns

> for _, group in ldoc.ipairs(item.retgroups) do
> for r in group:iter() do
> local type, ctypes = item:return_type(r)
> if type ~= '' then
> if r.text ~= '' then
`$(type)`: $(r.text)
> else
`$(type)`
> end
> else
$(r.text)
> end
> end
> end
> end

---
> end
Expand Down
Loading