Repository: nvim-lua/plenary.nvim
Pull request #538 (2023-11-05 07:39:53)

feat: add async.util.race to run funcs in race
It is annoying that we should create async functions to use `async.util.run_first`. I added `async.util.race` to run simple functions concurrently. `async.util.join` also accepts simple functions instead of async ones, so it is reasonable to use the same signature, I think.

```lua
local function sleep(msec)
  return function()
    a.util.sleep(msec)
    return msec
  end
end

local funcs = vim.tbl_map(sleep, { 200, 400, 300, 100 })

a.util.join(funcs)  --> { 200, 400, 300, 100 }
a.util.race(funcs)  --> 100
```

Conni2461: Thanks :) i rebased it because i wanted to rerun the test suite with your other change included :laughing: 

Patch:
diff --git a/lua/plenary/async/util.lua b/lua/plenary/async/util.lua
index ef33b26..c689ba3 100644
--- a/lua/plenary/async/util.lua
+++ b/lua/plenary/async/util.lua
@@ -76,14 +76,14 @@ M.join = function(async_fns)
   return results
 end
 
----Returns a future that when run will select the first async_function that finishes
----@param async_funs table: The async_function that you want to select
+---Returns a result from the future that finishes at the first
+---@param async_functions table: The futures that you want to select
 ---@return ...
-M.run_first = a.wrap(function(async_funs, step)
+M.run_first = a.wrap(function(async_functions, step)
   local ran = false
 
-  for _, future in ipairs(async_funs) do
-    assert(type(future) == "function", "type error :: future must be function")
+  for _, async_function in ipairs(async_functions) do
+    assert(type(async_function) == "function", "type error :: future must be function")
 
     local callback = function(...)
       if not ran then
@@ -92,10 +92,22 @@ M.run_first = a.wrap(function(async_funs, step)
       end
     end
 
-    future(callback)
+    async_function(callback)
   end
 end, 2)
 
+---Returns a result from the functions that finishes at the first
+---@param funcs table: The async functions that you want to select
+---@return ...
+M.race = function(funcs)
+  local async_functions = vim.tbl_map(function(func)
+    return function(callback)
+      a.run(func, callback)
+    end
+  end, funcs)
+  return M.run_first(async_functions)
+end
+
 M.run_all = function(async_fns, callback)
   a.run(function()
     M.join(async_fns)


Test patch:
diff --git a/tests/plenary/async/util_spec.lua b/tests/plenary/async/util_spec.lua
index 480f860..94ae324 100644
--- a/tests/plenary/async/util_spec.lua
+++ b/tests/plenary/async/util_spec.lua
@@ -47,4 +47,31 @@ describe("async await util", function()
       eq(ret, "didnt fail")
     end)
   end)
+
+  local function sleep(msec)
+    return function()
+      a.util.sleep(msec)
+      return msec
+    end
+  end
+
+  describe("race", function()
+    a.it("should return the first result", function()
+      local funcs = vim.tbl_map(sleep, { 300, 400, 100, 200 })
+      local result = a.util.race(funcs)
+      eq(result, 100)
+    end)
+  end)
+
+  describe("run_first", function()
+    a.it("should return the first result", function()
+      local async_functions = vim.tbl_map(function(num)
+        return function(callback)
+          return a.run(sleep(num), callback)
+        end
+      end, { 300, 400, 100, 200 })
+      local result = a.util.run_first(async_functions)
+      eq(result, 100)
+    end)
+  end)
 end)