@thigasdevelopment/luam 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lua/async.lua +6 -6
- package/lua/class.lua +239 -211
- package/lua/development-logs-client.lua +15 -9
- package/lua/development-logs-server.lua +41 -44
- package/lua/math.lua +21 -18
- package/lua/promise.lua +446 -0
- package/lua/string.lua +78 -62
- package/lua/table.lua +57 -49
- package/lua/threads.lua +326 -269
- package/lua/validate.lua +230 -185
- package/luam.mjs +301 -47
- package/package.json +1 -1
|
@@ -1,44 +1,41 @@
|
|
|
1
|
-
local luamDevelopmentEvent =
|
|
2
|
-
local luamDevelopmentMarker =
|
|
3
|
-
local luamMaximumMessageLength = __LUAM_MAX_MESSAGE_LENGTH__
|
|
4
|
-
local luamRateLimit = __LUAM_RATE_LIMIT__
|
|
5
|
-
local luamRateWindow = __LUAM_RATE_WINDOW_MS__
|
|
6
|
-
local luamRateBuckets = {}
|
|
7
|
-
|
|
8
|
-
addEvent(luamDevelopmentEvent, true)
|
|
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
|
-
addEventHandler("onPlayerQuit", root, function()
|
|
43
|
-
luamRateBuckets[source] = nil
|
|
44
|
-
end)
|
|
1
|
+
local luamDevelopmentEvent = 'luam:development-log';
|
|
2
|
+
local luamDevelopmentMarker = '__LUAM_DEV_LOG__';
|
|
3
|
+
local luamMaximumMessageLength = __LUAM_MAX_MESSAGE_LENGTH__; -- Replaced at build time with the limits the manifest sets.
|
|
4
|
+
local luamRateLimit = __LUAM_RATE_LIMIT__;
|
|
5
|
+
local luamRateWindow = __LUAM_RATE_WINDOW_MS__;
|
|
6
|
+
local luamRateBuckets = { };
|
|
7
|
+
|
|
8
|
+
addEvent (luamDevelopmentEvent, true);
|
|
9
|
+
|
|
10
|
+
---@param message string
|
|
11
|
+
---@param level number
|
|
12
|
+
addEventHandler (luamDevelopmentEvent, resourceRoot, function (message, level)
|
|
13
|
+
if (client == nil) or (source ~= resourceRoot) or (type (message) ~= 'string') or (#message > luamMaximumMessageLength) then return end
|
|
14
|
+
|
|
15
|
+
if (type (level) ~= 'number') or (level < 0) or (level > 4) or (level ~= math.floor (level)) then return end
|
|
16
|
+
|
|
17
|
+
local now = getTickCount ();
|
|
18
|
+
local bucket = luamRateBuckets[client];
|
|
19
|
+
|
|
20
|
+
if (bucket == nil) or (now < bucket.started) or ((now - bucket.started) >= luamRateWindow) then
|
|
21
|
+
bucket = { started = now, count = 0 };
|
|
22
|
+
luamRateBuckets[client] = bucket;
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if (bucket.count >= luamRateLimit) then return end
|
|
26
|
+
|
|
27
|
+
bucket.count = bucket.count + 1;
|
|
28
|
+
|
|
29
|
+
local record = {
|
|
30
|
+
environment = 'client',
|
|
31
|
+
level = level,
|
|
32
|
+
message = message,
|
|
33
|
+
resource = getResourceName (getThisResource ()),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
outputDebugString (luamDevelopmentMarker .. toJSON (record, true), 4);
|
|
37
|
+
end);
|
|
38
|
+
|
|
39
|
+
addEventHandler ('onPlayerQuit', root, function ()
|
|
40
|
+
luamRateBuckets[source] = nil;
|
|
41
|
+
end);
|
package/lua/math.lua
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
---@param value number
|
|
2
|
+
---@param minimum? number
|
|
3
|
+
---@param maximum? number
|
|
4
|
+
---@return number
|
|
5
|
+
function math.clamp (value, minimum, maximum)
|
|
6
|
+
value = tonumber (value);
|
|
7
|
+
if (not value) then
|
|
8
|
+
return 0;
|
|
9
|
+
end
|
|
3
10
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
end
|
|
11
|
+
minimum = tonumber (minimum) or value;
|
|
12
|
+
maximum = tonumber (maximum) or value;
|
|
7
13
|
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
if (minimum > maximum) then
|
|
15
|
+
minimum, maximum = maximum, minimum;
|
|
16
|
+
end
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
if (value < minimum) then
|
|
19
|
+
return minimum;
|
|
20
|
+
end
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
if (value > maximum) then
|
|
23
|
+
return maximum;
|
|
24
|
+
end
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
return maximum
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
return value
|
|
26
|
+
return value;
|
|
24
27
|
end
|
package/lua/promise.lua
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
local PENDING, FULFILLED, REJECTED = 'pending', 'fulfilled', 'rejected';
|
|
2
|
+
local MINIMUM_DELAY = 50; -- MTA never fires a timer sooner than this, so a shorter wait resumes on the next tick.
|
|
3
|
+
local AWAITING = { }; -- Yielded by a task waiting on a promise, so a thread pool leaves it to the scheduler.
|
|
4
|
+
|
|
5
|
+
local tasks = setmetatable ({ }, { __mode = 'k' });
|
|
6
|
+
local jobs = setmetatable ({ }, { __mode = 'k' });
|
|
7
|
+
local awaiting = setmetatable ({ }, { __mode = 'k' });
|
|
8
|
+
|
|
9
|
+
local queue, draining = { }, false;
|
|
10
|
+
local pulses, pulseTimer = { }, nil;
|
|
11
|
+
|
|
12
|
+
---@param ... any
|
|
13
|
+
---@return table
|
|
14
|
+
local function pack (...)
|
|
15
|
+
return { n = select ('#', ...), ... };
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
---@param values table
|
|
19
|
+
---@return ...
|
|
20
|
+
local function spread (values)
|
|
21
|
+
return unpack (values, 1, values.n);
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
---@param message any
|
|
25
|
+
local function report (message)
|
|
26
|
+
if (type (outputDebugString) == 'function') then
|
|
27
|
+
outputDebugString ('[Promise] ' .. tostring (message), 1);
|
|
28
|
+
return;
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
print ('[Promise] ' .. tostring (message));
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
---@param value any
|
|
35
|
+
---@return boolean
|
|
36
|
+
local function isPromise (value)
|
|
37
|
+
return (type (value) == 'table') and (getmetatable (value) == Promise);
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
---@return Promise
|
|
41
|
+
local function create ()
|
|
42
|
+
---@type Promise
|
|
43
|
+
return setmetatable ({ state = PENDING, values = { n = 0 }, handlers = { } }, Promise);
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
---@param promise Promise
|
|
47
|
+
---@param state string
|
|
48
|
+
---@param ... any
|
|
49
|
+
---@return boolean
|
|
50
|
+
local function settle (promise, state, ...)
|
|
51
|
+
if (promise.state ~= PENDING) then return false end
|
|
52
|
+
|
|
53
|
+
promise.state = state;
|
|
54
|
+
promise.values = pack (...);
|
|
55
|
+
|
|
56
|
+
local handlers = promise.handlers;
|
|
57
|
+
promise.handlers = { };
|
|
58
|
+
|
|
59
|
+
for index = 1, #handlers do
|
|
60
|
+
handlers[index] (promise);
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
return true;
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
local function drain ()
|
|
67
|
+
if (draining) then return end
|
|
68
|
+
|
|
69
|
+
draining = true;
|
|
70
|
+
while (#queue > 0) do
|
|
71
|
+
table.remove (queue, 1) ();
|
|
72
|
+
end
|
|
73
|
+
draining = false;
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
---@param job fun()
|
|
77
|
+
local function schedule (job)
|
|
78
|
+
queue[#queue + 1] = job;
|
|
79
|
+
drain ();
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
---@param routine thread
|
|
83
|
+
---@param ... any
|
|
84
|
+
---@return boolean
|
|
85
|
+
local function resumeTask (routine, ...)
|
|
86
|
+
if (coroutine.status (routine) ~= 'suspended') then return false end
|
|
87
|
+
|
|
88
|
+
local promise = tasks[routine];
|
|
89
|
+
local outcome = pack (coroutine.resume (routine, ...));
|
|
90
|
+
|
|
91
|
+
if (not outcome[1]) then
|
|
92
|
+
if (not promise) or (#promise.handlers == 0) then -- Nobody is waiting on it, so the reason would vanish.
|
|
93
|
+
report (outcome[2]);
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if (promise) then
|
|
97
|
+
settle (promise, REJECTED, outcome[2]);
|
|
98
|
+
end
|
|
99
|
+
return false;
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if (promise) and (coroutine.status (routine) == 'dead') then
|
|
103
|
+
settle (promise, FULFILLED, unpack (outcome, 2, outcome.n));
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
return true;
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
---@param routine thread
|
|
110
|
+
---@param ... any
|
|
111
|
+
---@return ...
|
|
112
|
+
local function clearAwait (routine, ...)
|
|
113
|
+
awaiting[routine] = nil;
|
|
114
|
+
return ...;
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
---@param promise Promise
|
|
118
|
+
---@return boolean, ...
|
|
119
|
+
local function wait (promise)
|
|
120
|
+
local routine = coroutine.running ();
|
|
121
|
+
if (not routine) then
|
|
122
|
+
error ('"await" is only valid inside an async function, and this call is not running inside one.', 0);
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if (not tasks[routine]) then
|
|
126
|
+
error ('"await" is only valid inside a coroutine the promise scheduler drives.', 0);
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if (promise.state ~= PENDING) then
|
|
130
|
+
return (promise.state == FULFILLED), spread (promise.values);
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
local handlers = promise.handlers;
|
|
134
|
+
handlers[#handlers + 1] = function (source)
|
|
135
|
+
schedule (function ()
|
|
136
|
+
resumeTask (routine, (source.state == FULFILLED), spread (source.values));
|
|
137
|
+
end);
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
awaiting[routine] = true;
|
|
141
|
+
return clearAwait (routine, coroutine.yield (AWAITING));
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
---@param ok boolean
|
|
145
|
+
---@param ... any
|
|
146
|
+
---@return ...
|
|
147
|
+
local function unwrap (ok, ...)
|
|
148
|
+
if (not ok) then
|
|
149
|
+
error ((...), 0);
|
|
150
|
+
end
|
|
151
|
+
return ...;
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
local function tick ()
|
|
155
|
+
for index = 1, #pulses do
|
|
156
|
+
local job = pulses[index];
|
|
157
|
+
if (job ~= nil) then
|
|
158
|
+
job ();
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
---@class Promise
|
|
164
|
+
---@field state string
|
|
165
|
+
---@field values table
|
|
166
|
+
---@field handlers table
|
|
167
|
+
---@field new fun(executor: fun(resolve: fun(...): any, reject: fun(...): any): any): Promise
|
|
168
|
+
---@field spawn fun(func: fun(...): any, ...): Promise
|
|
169
|
+
---@field resolve fun(...): Promise
|
|
170
|
+
---@field reject fun(...): Promise
|
|
171
|
+
---@field all fun(list: table): Promise
|
|
172
|
+
---@field race fun(list: table): Promise
|
|
173
|
+
---@field await fun(promise: Promise): any
|
|
174
|
+
---@field settle fun(promise: Promise): boolean, any
|
|
175
|
+
---@field delay fun(milliseconds: number): Promise
|
|
176
|
+
---@field next fun(self: Promise, onFulfilled: fun(...): any, onRejected?: fun(reason: any): any): Promise
|
|
177
|
+
---@field catch fun(self: Promise, onRejected: fun(reason: any): any): Promise
|
|
178
|
+
Promise = {
|
|
179
|
+
---@param executor fun(resolve: fun(...): any, reject: fun(...): any): any
|
|
180
|
+
---@return Promise
|
|
181
|
+
new = function (executor)
|
|
182
|
+
local promise = create ();
|
|
183
|
+
local executorType = type (executor);
|
|
184
|
+
if (executorType ~= 'function') then
|
|
185
|
+
error ('bad argument #1 to \'new\' (\'function\' expected got \'' .. executorType .. '\').', 2);
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
local ok, reason = pcall (executor, function (...)
|
|
189
|
+
settle (promise, FULFILLED, ...);
|
|
190
|
+
end, function (...)
|
|
191
|
+
settle (promise, REJECTED, ...);
|
|
192
|
+
end);
|
|
193
|
+
|
|
194
|
+
if (not ok) then
|
|
195
|
+
settle (promise, REJECTED, reason);
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
return promise;
|
|
199
|
+
end,
|
|
200
|
+
|
|
201
|
+
---@param func fun(...): any
|
|
202
|
+
---@param ... any
|
|
203
|
+
---@return Promise
|
|
204
|
+
spawn = function (func, ...)
|
|
205
|
+
local promise = create ();
|
|
206
|
+
|
|
207
|
+
if (type (func) ~= 'function') then
|
|
208
|
+
settle (promise, REJECTED, 'Promise.spawn expects a function.');
|
|
209
|
+
return promise;
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
local routine = coroutine.create (func);
|
|
213
|
+
tasks[routine] = promise;
|
|
214
|
+
|
|
215
|
+
resumeTask (routine, ...);
|
|
216
|
+
return promise;
|
|
217
|
+
end,
|
|
218
|
+
|
|
219
|
+
---@param ... any
|
|
220
|
+
---@return Promise
|
|
221
|
+
resolve = function (...)
|
|
222
|
+
local value = ...;
|
|
223
|
+
if (select ('#', ...) == 1) and (isPromise (value)) then
|
|
224
|
+
return value;
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
local promise = create ();
|
|
228
|
+
settle (promise, FULFILLED, ...);
|
|
229
|
+
return promise;
|
|
230
|
+
end,
|
|
231
|
+
|
|
232
|
+
---@param ... any
|
|
233
|
+
---@return Promise
|
|
234
|
+
reject = function (...)
|
|
235
|
+
local promise = create ();
|
|
236
|
+
settle (promise, REJECTED, ...);
|
|
237
|
+
return promise;
|
|
238
|
+
end,
|
|
239
|
+
|
|
240
|
+
---@param list table
|
|
241
|
+
---@return Promise
|
|
242
|
+
all = function (list)
|
|
243
|
+
local result = create ();
|
|
244
|
+
local values, remaining = { }, #list;
|
|
245
|
+
|
|
246
|
+
if (remaining == 0) then
|
|
247
|
+
settle (result, FULFILLED, values);
|
|
248
|
+
return result;
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
for index = 1, #list do
|
|
252
|
+
Promise.resolve (list[index]):next (function (value)
|
|
253
|
+
values[index] = value;
|
|
254
|
+
remaining = remaining - 1;
|
|
255
|
+
|
|
256
|
+
if (remaining == 0) then
|
|
257
|
+
settle (result, FULFILLED, values);
|
|
258
|
+
end
|
|
259
|
+
end, function (...)
|
|
260
|
+
settle (result, REJECTED, ...);
|
|
261
|
+
end);
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
return result;
|
|
265
|
+
end,
|
|
266
|
+
|
|
267
|
+
---@param list table
|
|
268
|
+
---@return Promise
|
|
269
|
+
race = function (list)
|
|
270
|
+
local result = create ();
|
|
271
|
+
for index = 1, #list do
|
|
272
|
+
Promise.resolve (list[index]):next (function (...)
|
|
273
|
+
settle (result, FULFILLED, ...);
|
|
274
|
+
end, function (...)
|
|
275
|
+
settle (result, REJECTED, ...);
|
|
276
|
+
end);
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
return result;
|
|
280
|
+
end,
|
|
281
|
+
|
|
282
|
+
---@param promise Promise
|
|
283
|
+
---@return ...
|
|
284
|
+
await = function (promise)
|
|
285
|
+
if (not isPromise (promise)) then
|
|
286
|
+
return promise;
|
|
287
|
+
end
|
|
288
|
+
return unwrap (wait (promise));
|
|
289
|
+
end,
|
|
290
|
+
|
|
291
|
+
---@param promise Promise
|
|
292
|
+
---@return boolean, ...
|
|
293
|
+
settle = function (promise)
|
|
294
|
+
if (not isPromise (promise)) then
|
|
295
|
+
return true, promise;
|
|
296
|
+
end
|
|
297
|
+
return wait (promise);
|
|
298
|
+
end,
|
|
299
|
+
|
|
300
|
+
---@param milliseconds number
|
|
301
|
+
---@return Promise
|
|
302
|
+
delay = function (milliseconds)
|
|
303
|
+
milliseconds = tonumber (milliseconds) or 0;
|
|
304
|
+
if (milliseconds < MINIMUM_DELAY) then
|
|
305
|
+
milliseconds = MINIMUM_DELAY;
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
return Promise.new (function (resolve)
|
|
309
|
+
setTimer (resolve, milliseconds, 1);
|
|
310
|
+
end);
|
|
311
|
+
end,
|
|
312
|
+
|
|
313
|
+
---@param routine thread
|
|
314
|
+
---@return Promise
|
|
315
|
+
adopt = function (routine)
|
|
316
|
+
local promise = create ();
|
|
317
|
+
tasks[routine] = promise;
|
|
318
|
+
jobs[routine] = true;
|
|
319
|
+
|
|
320
|
+
return promise;
|
|
321
|
+
end,
|
|
322
|
+
|
|
323
|
+
---@param routine thread
|
|
324
|
+
---@param ... any
|
|
325
|
+
---@return boolean
|
|
326
|
+
resume = function (routine, ...)
|
|
327
|
+
return resumeTask (routine, ...);
|
|
328
|
+
end,
|
|
329
|
+
|
|
330
|
+
---@param routine thread
|
|
331
|
+
---@return boolean
|
|
332
|
+
isAwaiting = function (routine)
|
|
333
|
+
return (routine ~= nil) and (awaiting[routine] == true);
|
|
334
|
+
end,
|
|
335
|
+
|
|
336
|
+
---@param callback fun()
|
|
337
|
+
---@return fun()
|
|
338
|
+
pulse = function (callback)
|
|
339
|
+
pulses[#pulses + 1] = callback;
|
|
340
|
+
if (pulseTimer == nil) then
|
|
341
|
+
pulseTimer = setTimer (tick, MINIMUM_DELAY, 0);
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
return callback;
|
|
345
|
+
end,
|
|
346
|
+
|
|
347
|
+
---@param callback fun()
|
|
348
|
+
unpulse = function (callback)
|
|
349
|
+
for index = #pulses, 1, -1 do
|
|
350
|
+
if (pulses[index] == callback) then
|
|
351
|
+
table.remove (pulses, index);
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
if (#pulses == 0) and (pulseTimer ~= nil) then
|
|
356
|
+
killTimer (pulseTimer);
|
|
357
|
+
pulseTimer = nil;
|
|
358
|
+
end
|
|
359
|
+
end,
|
|
360
|
+
|
|
361
|
+
---@param self Promise
|
|
362
|
+
---@param onFulfilled fun(...): any
|
|
363
|
+
---@param onRejected? fun(reason: any): any
|
|
364
|
+
---@return Promise
|
|
365
|
+
next = function (self, onFulfilled, onRejected)
|
|
366
|
+
local chained = create ();
|
|
367
|
+
|
|
368
|
+
local function forward (source)
|
|
369
|
+
local handler = onRejected;
|
|
370
|
+
if (source.state == FULFILLED) then
|
|
371
|
+
handler = onFulfilled;
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
if (type (handler) ~= 'function') then
|
|
375
|
+
settle (chained, source.state, spread (source.values));
|
|
376
|
+
return;
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
local outcome = pack (pcall (handler, spread (source.values)));
|
|
380
|
+
if (not outcome[1]) then
|
|
381
|
+
settle (chained, REJECTED, outcome[2]);
|
|
382
|
+
return;
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
local value = outcome[2];
|
|
386
|
+
if (isPromise (value)) then
|
|
387
|
+
value:next (function (...)
|
|
388
|
+
settle (chained, FULFILLED, ...);
|
|
389
|
+
end, function (...)
|
|
390
|
+
settle (chained, REJECTED, ...);
|
|
391
|
+
end);
|
|
392
|
+
return;
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
settle (chained, FULFILLED, unpack (outcome, 2, outcome.n));
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
if (self.state == PENDING) then
|
|
399
|
+
self.handlers[#self.handlers + 1] = forward;
|
|
400
|
+
else
|
|
401
|
+
forward (self);
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
return chained;
|
|
405
|
+
end,
|
|
406
|
+
|
|
407
|
+
---@param self Promise
|
|
408
|
+
---@param onRejected fun(reason: any): any
|
|
409
|
+
---@return Promise
|
|
410
|
+
catch = function (self, onRejected)
|
|
411
|
+
return self:next (nil, onRejected);
|
|
412
|
+
end,
|
|
413
|
+
};
|
|
414
|
+
Promise.__index = Promise;
|
|
415
|
+
|
|
416
|
+
---@param milliseconds number
|
|
417
|
+
---@return Promise
|
|
418
|
+
function delay (milliseconds)
|
|
419
|
+
return Promise.delay (milliseconds);
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
---@param milliseconds number
|
|
423
|
+
function sleep (milliseconds)
|
|
424
|
+
milliseconds = tonumber (milliseconds) or 0;
|
|
425
|
+
|
|
426
|
+
local routine = coroutine.running ();
|
|
427
|
+
if (routine) and (jobs[routine]) then
|
|
428
|
+
if (milliseconds < 1) then
|
|
429
|
+
coroutine.yield ();
|
|
430
|
+
return;
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
local start = getTickCount ();
|
|
434
|
+
repeat
|
|
435
|
+
coroutine.yield ();
|
|
436
|
+
until (getTickCount () - start) >= milliseconds
|
|
437
|
+
|
|
438
|
+
return;
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
if (not routine) or (not tasks[routine]) then
|
|
442
|
+
error ('"sleep" is only valid inside an async function or a thread pool job.', 0);
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
return Promise.await (Promise.delay (milliseconds));
|
|
446
|
+
end
|