1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
local F = vim.F
local util = require('vim._core.util')
--- @class (internal) vim.async.Future<R>
--- @field private _callbacks table<integer, fun(err?: any, ...: R...)>
--- @field private _callback_pos integer
--- @field private _err? any
--- @field private _result? R[] & { n: integer }
local Future = {}
Future.__index = Future
function Future:completed()
return self._err ~= nil or self._result ~= nil
end
function Future:result()
if not self:completed() then
error('Future has not completed', 2)
end
if self._err ~= nil then
return false, self._err
else
assert(self._result)
return true, F.unpack_len(self._result)
end
end
--- @param callback fun(err?: any, ...: R...)
--- @return fun()
function Future:on_complete(callback)
if self:completed() then
-- Already completed or closed
if self._err ~= nil then
callback(self._err)
else
assert(self._result)
callback(nil, F.unpack_len(self._result))
end
return function() end
end
local id = self._callback_pos
self._callback_pos = id + 1
self._callbacks[id] = callback
return function()
self._callbacks[id] = nil
end
end
--- @param err? any
--- @param ... R...
function Future:complete(err, ...)
if self:completed() then
error('Future is already completed', 2)
end
if err ~= nil then
self._err = err
else
self._result = F.pack_len(...)
end
local callbacks = self._callbacks
self._callbacks = {}
local errs = {} -- Need to use pairs to avoid gaps caused by removed callbacks
for _, cb in pairs(callbacks) do
local ok, cb_err = pcall(cb, err, ...)
if not ok then
errs[#errs + 1] = util._stringify_error(util._normalize_error(cb_err))
end
end
if #errs > 0 then
error(table.concat(errs, '\n'), 0)
end
end
--- @return vim.async.Future<any>
return function()
return setmetatable({
_callbacks = {},
_callback_pos = 1,
}, Future)
end
|