1 local promise_methods
= {};
2 local promise_mt
= { __name
= "promise", __index
= promise_methods
};
4 local xpcall
= require
"util.xpcall".xpcall
;
6 function promise_mt
:__tostring()
7 return "promise (" .. (self
._state
or "invalid") .. ")";
10 local function is_promise(o
)
11 local mt
= getmetatable(o
);
12 return mt
== promise_mt
;
15 local function wrap_handler(f
, resolve
, reject
, default
)
19 return function (param
)
20 local ok
, ret
= xpcall(f
, debug
.traceback
, param
);
30 local function next_pending(self
, on_fulfilled
, on_rejected
, resolve
, reject
)
31 table.insert(self
._pending_on_fulfilled
, wrap_handler(on_fulfilled
, resolve
, reject
, resolve
));
32 table.insert(self
._pending_on_rejected
, wrap_handler(on_rejected
, resolve
, reject
, reject
));
35 local function next_fulfilled(promise
, on_fulfilled
, on_rejected
, resolve
, reject
) -- luacheck: ignore 212/on_rejected
36 wrap_handler(on_fulfilled
, resolve
, reject
, resolve
)(promise
.value
);
39 local function next_rejected(promise
, on_fulfilled
, on_rejected
, resolve
, reject
) -- luacheck: ignore 212/on_fulfilled
40 wrap_handler(on_rejected
, resolve
, reject
, reject
)(promise
.reason
);
43 local function promise_settle(promise
, new_state
, new_next
, cbs
, value
)
44 if promise
._state
~= "pending" then
47 promise
._state
= new_state
;
48 promise
._next
= new_next
;
49 for _
, cb
in ipairs(cbs
) do
52 -- No need to keep references to callbacks
53 promise
._pending_on_fulfilled
= nil;
54 promise
._pending_on_rejected
= nil;
58 local function new_resolve_functions(p
)
59 local resolved
= false;
60 local function _resolve(v
)
61 if resolved
then return; end
64 v
:next(new_resolve_functions(p
));
65 elseif promise_settle(p
, "fulfilled", next_fulfilled
, p
._pending_on_fulfilled
, v
) then
70 local function _reject(e
)
71 if resolved
then return; end
73 if promise_settle(p
, "rejected", next_rejected
, p
._pending_on_rejected
, e
) then
77 return _resolve
, _reject
;
81 local p
= setmetatable({ _state
= "pending", _next
= next_pending
, _pending_on_fulfilled
= {}, _pending_on_rejected
= {} }, promise_mt
);
83 local resolve
, reject
= new_resolve_functions(p
);
84 local ok
, ret
= pcall(f
, resolve
, reject
);
85 if not ok
and p
._state
== "pending" then
92 local function all(promises
)
93 return new(function (resolve
, reject
)
94 local count
, total
, results
= 0, #promises
, {};
96 promises
[i
]:next(function (v
)
99 if count
== total
then
107 local function race(promises
)
108 return new(function (resolve
, reject
)
109 for i
= 1, #promises
do
110 promises
[i
]:next(resolve
, reject
);
115 local function resolve(v
)
116 return new(function (_resolve
)
121 local function reject(v
)
122 return new(function (_
, _reject
)
127 local function try(f
)
128 return resolve():next(function () return f(); end);
131 function promise_methods
:next(on_fulfilled
, on_rejected
)
132 return new(function (resolve
, reject
) --luacheck: ignore 431/resolve 431/reject
133 self
:_next(on_fulfilled
, on_rejected
, resolve
, reject
);
137 function promise_methods
:catch(on_rejected
)
138 return self
:next(nil, on_rejected
);
141 function promise_methods
:finally(on_finally
)
142 local function _on_finally(value
) on_finally(); return value
; end
143 local function _on_catch_finally(err
) on_finally(); return reject(err
); end
144 return self
:next(_on_finally
, _on_catch_finally
);
154 is_promise
= is_promise
;