{"id":15657865,"url":"https://github.com/willothy/micro-async.nvim","last_synced_at":"2025-03-30T02:22:07.017Z","repository":{"id":197537245,"uuid":"697615537","full_name":"willothy/micro-async.nvim","owner":"willothy","description":"Ultra-simple async library for Neovim, with cancellation support","archived":false,"fork":false,"pushed_at":"2024-01-10T00:48:56.000Z","size":76,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-05T04:48:58.491Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willothy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-28T05:47:17.000Z","updated_at":"2024-09-26T20:28:33.000Z","dependencies_parsed_at":"2023-10-11T14:29:51.561Z","dependency_job_id":"5e20590a-1065-41cf-86a7-2dcc69d23fc9","html_url":"https://github.com/willothy/micro-async.nvim","commit_stats":null,"previous_names":["willothy/micro-async.nvim"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willothy%2Fmicro-async.nvim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willothy%2Fmicro-async.nvim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willothy%2Fmicro-async.nvim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willothy%2Fmicro-async.nvim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willothy","download_url":"https://codeload.github.com/willothy/micro-async.nvim/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246266630,"owners_count":20749826,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-03T13:10:02.218Z","updated_at":"2025-03-30T02:22:07.003Z","avatar_url":"https://github.com/willothy.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003emicro-async.nvim\u003c/h1\u003e\n\u003c/div\u003e\n\nExtremely simple async library for Neovim.\n\n## Features\n\n- One coroutine per task, minimal overhead\n- Supports cancellation, with or without custom handles\n- Simple interface to create custom async functions\n\n## Installation\n\n```lua\nrequire(\"lazy\").setup({\n  --- ...\n  { \"willothy/micro-async.nvim\" } -- no need to call setup()\n})\n```\n\n## Usage\n\n### `Task`\n\nTop-level tasks are represented by a `Task` object. The `Task` type has three methods:\n\n- `Task:resume(...):Cancellable?`: Resumes the task with the provided arguments (used internally, you shouldn't need to call this)\n- `Task:cancel()`: Cancels the task, if it has not already been cancelled. Calls the running `Cancellable`'s `:cancel()` method, if any.\n- `Task:is_cancelled():boolean`: Returns true if the task has been cancelled, false otherwise\n\nWhen a top-level task is cancelled, in addition to calling the running `Cancellable`'s `:cancel()` method, no further calls to `Task:resume()` will resume a task's coroutine. Therefore, a cancelled task is effectively `\"dead\"` even if the status\nof its coroutine is not `\"dead\"`, and any lingering sheduled / delayed resumes from non-cancelled luv handles will not cause a task to resume after cancellation.\n\nTasks can be created through two functions:\n\n- `a.run(fn: fun(...):..., cb: fun(...)): Task`: Runs the provided function in an async context, and calls the callback with the result. Returns the created `Task` handle.\n- `a.void(fn: fun(...)): fun(...): Task`: Creates a function that can be called from a non-async context, but cannot return any values as it is non-blocking. The resulting function returns a `Task`.\n\n### `Cancellable`\n\nCancellable functions (mainly libuv functions that return `handles`) are implemented with a `Cancellable` interface, inspired by [`async.nvim`](https://github.com/lewis6991/async.nvim)'s `async_T`.\n\nThe `Cancellable` interface matches the cancellation API of top-level tasks, which allows top-level tasks to be used in place of `Cancellable` handles within nested tasks.\n\nCreating a cancellable function is as simple as returning (or yielding, in an async context) a `handle` object that implements the `Cancellable` interface (see [examples](#examples)).\n\nRequired methods:\n\n- `Cancellable:cancel()`: Cancels the running function, if it has not already been cancelled. Called internally by `Task:cancel()`.\n- `Cancellable:is_cancelled():boolean`: Returns true if the call has been cancelled, false otherwise\n\n### Examples\n\n```lua\n\nlocal a = require(\"micro-async\")\n\n-- Run a function asynchronously and call the callback with the result.\na.run(\n  function()\n    -- run a system command\n    local res = a.system({ \"ls\" }, {})\n\n    -- wait 2 seconds\n    a.defer(2000)\n\n    -- return the result, calling the callback\n    return res.stdout\n  end,\n  vim.print -- print the result\n)\n\n-- Yield to the scheduler to call the nvim API safely from luv callbacks\na.run(\n  function()\n    --- Use a timer to simulate resuming from a luv callback\n    local t = vim.uv.new_timer()\n    coroutine.yield(t:start(1000, 0, a.callback()))\n    t:close()\n    a.schedule() -- yield to `vim.schedule()` to ensure the nvim API is safe to call\n\n    return vim.api.nvim_exec2(\"echo 'hello world'\", { output = true }).output\n  end,\n  vim.print\n)\n\n-- Count lines of code using `tokei`, and format the output with `jq`\na.run(\n  function()\n    local tokei = a.system({ \"tokei\", \"--output\", \"json\" }, { text = true })\n\n    if tokei.code ~= 0 then\n      return tokei.stderr\n    end\n\n    local jq = a.system({ \"jq\" }, {\n      text = true,\n      stdin = tokei.stdout,\n    })\n\n    if jq.code ~= 0 then\n      return jq.stderr\n    end\n\n    return jq.stdout\n  end,\n  vim.print\n)\n\n-- Cancel a task before it finishes\nlocal task = a.void(function()\n  a.defer(1000)\n  vim.notify(\"Did not cancel\", vim.log.levels.ERROR)\nend)()\ntask:cancel()\n\n-- Create wrap function on `vim.system` and yield value on exit.\n-- The `callback` parameter is an additional parameter added to the end of wrapped function,\n-- to yield value on `vim.system` exit.\nlocal system_wrap = a.wrap(function(cmd, opts, callback)\n  vim.system(cmd, opts, function(system_completed)\n    callback(system_completed)\n  end)\nend, 3)\na.run(\n  function()\n    -- returns the yield value just like sync function.\n    local system_completed = system_wrap({ \"tokei\", \"--output\", \"json\" }, { text = true })\n    return system_completed\n  end,\n  function(system_completed)\n    vim.print(vim.inspect(system_completed))\n  end\n)\n\n-- Create your own cancellable function\n-- Use `custom_defer` just like a normal async function. When\n-- it is running and the top-level task is cancelled, its cancel()\n-- function will be called.\n--\n-- This function should behave identically to `a.defer` in the above example\nlocal function custom_defer(timeout)\n  local timer = vim.defer_fn(timeout, a.callback())\n\n  local handle = {}\n\n  -- this is Cancellable:is_cancelled()\n  function handle:is_cancelled()\n    return timer:is_active()\n  end\n\n  -- this is Cancellable:cancel()\n  function handle:cancel()\n    if not timer:is_closing()\n      timer:close()\n    end\n  end\n\n  return coroutine.yield(handle)\nend\n\n-- Call any callback-style function asynchronously in a task,\n-- without needing to wrap it previously in `a.wrap()`\nlocal function stat_type(path)\n  -- a.callback() creates a callback that resumes the current task\n  -- this is how `a.wrap` resumes tasks internally\n  local stat = vim.uv.fs_stat(path, a.callback())\n  if stat then\n    return stat.type\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillothy%2Fmicro-async.nvim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillothy%2Fmicro-async.nvim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillothy%2Fmicro-async.nvim/lists"}