Comparison

util/promise.lua @ 9456:d54a0e129af8

util.promise: ES6-like API for promises
author Matthew Wild <mwild1@gmail.com>
date Wed, 10 Oct 2018 17:45:19 +0100
child 9510:8ef46d09386a
comparison
equal deleted inserted replaced
9455:c62c983b8be3 9456:d54a0e129af8
1 local promise_methods = {};
2 local promise_mt = { __name = "promise", __index = promise_methods };
3
4 local function is_promise(o)
5 local mt = getmetatable(o);
6 return mt == promise_mt;
7 end
8
9 local function next_pending(self, on_fulfilled, on_rejected)
10 table.insert(self._pending_on_fulfilled, on_fulfilled);
11 table.insert(self._pending_on_rejected, on_rejected);
12 end
13
14 local function next_fulfilled(promise, on_fulfilled, on_rejected) -- luacheck: ignore 212/on_rejected
15 on_fulfilled(promise.value);
16 end
17
18 local function next_rejected(promise, on_fulfilled, on_rejected) -- luacheck: ignore 212/on_fulfilled
19 on_rejected(promise.reason);
20 end
21
22 local function promise_settle(promise, new_state, new_next, cbs, value)
23 if promise._state ~= "pending" then
24 return;
25 end
26 promise._state = new_state;
27 promise._next = new_next;
28 for _, cb in ipairs(cbs) do
29 cb(value);
30 end
31 return true;
32 end
33
34 local function new_resolve_functions(p)
35 local resolved = false;
36 local function _resolve(v)
37 if resolved then return; end
38 resolved = true;
39 if is_promise(v) then
40 v:next(new_resolve_functions(p));
41 elseif promise_settle(p, "fulfilled", next_fulfilled, p._pending_on_fulfilled, v) then
42 p.value = v;
43 end
44
45 end
46 local function _reject(e)
47 if resolved then return; end
48 resolved = true;
49 if promise_settle(p, "rejected", next_rejected, p._pending_on_rejected, e) then
50 p.reason = e;
51 end
52 end
53 return _resolve, _reject;
54 end
55
56 local function new(f)
57 local p = setmetatable({ _state = "pending", _next = next_pending, _pending_on_fulfilled = {}, _pending_on_rejected = {} }, promise_mt);
58 if f then
59 local resolve, reject = new_resolve_functions(p);
60 local ok, ret = pcall(f, resolve, reject);
61 if not ok and p._state == "pending" then
62 reject(ret);
63 end
64 end
65 return p;
66 end
67
68 local function wrap_handler(f, resolve, reject)
69 return function (param)
70 local ok, ret = pcall(f, param);
71 if ok then
72 resolve(ret);
73 else
74 reject(ret);
75 end
76 end;
77 end
78
79 function promise_methods:next(on_fulfilled, on_rejected)
80 return new(function (resolve, reject)
81 self:_next(
82 on_fulfilled and wrap_handler(on_fulfilled, resolve, reject) or nil,
83 on_rejected and wrap_handler(on_rejected, resolve, reject) or nil
84 );
85 end);
86 end
87
88 function promise_methods:catch(on_rejected)
89 return self:next(nil, on_rejected);
90 end
91
92 local function all(promises)
93 return new(function (resolve, reject)
94 local count, total, results = 0, #promises, {};
95 for i = 1, total do
96 promises[i]:next(function (v)
97 results[i] = v;
98 count = count + 1;
99 if count == total then
100 resolve(results);
101 end
102 end, reject);
103 end
104 end);
105 end
106
107 local function race(promises)
108 return new(function (resolve, reject)
109 for i = 1, #promises do
110 promises[i]:next(resolve, reject);
111 end
112 end);
113 end
114
115 local function resolve(v)
116 return new(function (_resolve)
117 _resolve(v);
118 end);
119 end
120
121 local function reject(v)
122 return new(function (_reject)
123 _reject(v);
124 end);
125 end
126
127 return {
128 new = new;
129 resolve = resolve;
130 reject = reject;
131 all = all;
132 race = race;
133 }