Initial forked version

This commit is contained in:
Amber's Careware 2022-10-24 20:14:27 -06:00
parent 2c6f433903
commit 558b61fe3d
12 changed files with 259 additions and 92 deletions

1
.github/FUNDING.yml vendored
View file

@ -1 +0,0 @@
patreon: erynlynn

3
.gitignore vendored
View file

@ -2,4 +2,5 @@
node_modules/
.vscode
build
roblox.toml
roblox.toml
sourcemap.json

View file

@ -1,5 +1,14 @@
# Changelog
## FORKED VERSION [4.0.0]
- Added `any` type annotations to many variables in the internal implementation in order to prevent crashes while using this library
- Added `--!nonstrict` compiler directive at the top of the script to expose types
- Added library-user-facing typings for `Promise`, `Error`, `Status` and the library itself. This fork uses the `(object :: any) :: PublicType` idiom to simplify the types to the library, allowing for better autocompletion and type annotations in a strict-mode Luau codebase
No other changes have been made to the library's runtime behavior.
Please notify me through the issues section if any issues are encountered related to type safety or crashes. Otherwise, please send any issues related to runtime behavior to the [original repository](https://github.com/evaera/roblox-lua-promise).
## [4.0.0]
### Changed
- `Promise:finally` no longer observes a rejection from a Promise. Calling `Promise:finally` is mostly transparent now.

View file

@ -1,5 +1,6 @@
MIT License
Copyright (c) 2022 Amber Grace
Copyright (c) 2019 Eryn L. K.
Permission is hereby granted, free of charge, to any person obtaining a copy

View file

@ -1,11 +1,10 @@
<div align="center">
<h1>Roblox Lua Promise</h1>
<h1>Roblox Luau Promise</h1>
<p>An implementation of <code>Promise</code> similar to Promise/A+.</p>
<a href="https://eryn.io/roblox-lua-promise/"><strong>View docs</strong></a>
</div>
<!--moonwave-hide-before-this-line-->
## Why you should use Promises
The way Roblox models asynchronous operations by default is by yielding (stopping) the thread and then resuming it when the future value is available. This model is not ideal because:
@ -20,3 +19,9 @@ This Promise implementation attempts to satisfy these traits:
* An object that represents a unit of asynchronous work
* Composability
* Predictable timing
## FORKED VERSION
This is a forked version of [Evaera's Promise library](https://github.com/evaera/roblox-lua-promise) with type annotations added to allow this library to be used in a strict-mode [Luau](https://luau-lang.org) project. Type annotations are imperfect due to limitations with the Luau language, but should at least cover intellisense and basic static analysis.
Please notify me through the issues section if any issues are encountered related to type safety or crashes. Otherwise, please send any issues related to runtime behavior to the [original repository](https://github.com/evaera/roblox-lua-promise).

26
lib/LICENSE.luau Normal file
View file

@ -0,0 +1,26 @@
return [[
MIT License
Copyright (c) 2022 Amber Grace
Copyright (c) 2019 Eryn L. K.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]

View file

@ -1,13 +1,144 @@
--!nonstrict
--[[
An implementation of Promises similar to Promise/A+.
An implementation of Promises similar to Promise/A+
Original library by Eryn L. K.
Edit by Amber Grace (@DataBrain) to include custom type annotations & extensions
Forked from github repository: evaera/roblox-lua-promise
Forked from release V4.0.0 (Mar 2nd 2022).
I do not intend to maintain
this forked version of the library unless a major bug or exploit is
found with the original library that needs to be patched here.
Licensed under MIT License (see nested module for full license)
]]
export type Status = "Started" | "Resolved" | "Rejected" | "Cancelled"
export type Executor = (
resolve: (...any) -> (),
reject: (...any) -> (),
onCancel: (abortHandler: (() -> ())?) -> boolean
) -> ()
export type Promise = {
timeout: (self: Promise, seconds: number, rejectionValue: any?) -> Promise,
getStatus: (self: Promise) -> Status,
andThen: (
self: Promise,
successHandler: (...any) -> ...any,
failureHandler: ((...any) -> ...any)?
) -> Promise,
catch: (
self: Promise,
failureHandler: (...any) -> ...any
) -> Promise,
tap: (
self: Promise,
tapHandler: (...any) -> ...any
) -> Promise,
andThenCall: <T...>(
self: Promise,
callback: (T...) -> any,
T...
) -> Promise,
andThenReturn: (
self: Promise,
...any
) -> Promise,
cancel: (self: Promise) -> (),
finally: (
self: Promise,
finallyHandler: (status: Status) -> ...any
) -> Promise,
finallyCall: <T...>(
self: Promise,
callback: (T...) -> any,
T...
) -> Promise,
finallyReturn: (
self: Promise,
...any
) -> Promise,
awaitStatus: (
self: Promise
) -> (Status, ...any),
await: (self: Promise) -> (boolean, ...any),
expect: (self: Promise) -> ...any,
now: (self: Promise, rejectionValue: any?) -> Promise,
}
export type ErrorKind = "ExecutionError" | "AlreadyCancelled" | "NotResolvedInTime" | "TimedOut"
export type ErrorOptions = {
error: string?,
trace: string?,
context: string?,
kind: ErrorKind
}
export type Error = {
kind: ErrorKind,
trace: string?,
context: string?,
parent: Error?,
error: string,
createdTick: number,
createdTrace: string,
extend: (self: Error, options: ErrorOptions) -> Error,
getErrorChain: (self: Error) -> {Error},
}
type PromiseLib = {
new: (executor: Executor) -> Promise,
defer: (executor: Executor) -> Promise,
resolve: (...any) -> Promise,
reject: (...any) -> Promise,
try: <T...>(
callback: (T...) -> ...any,
T...
) -> Promise,
all: (promises: {Promise}) -> Promise,
fold: (
list: {any},
reducer: (accumulator: any, value: any, index: number) -> any,
initialValue: any
) -> (),
some: (promises: {Promise}, count: number) -> Promise,
any: (promises: {Promise}) -> Promise,
allSettled: (promises: {Promise}) -> Promise,
race: (promises: {Promise}) -> Promise,
each: (
list: {any},
predicate: (value: any, index: number) -> any
) -> Promise,
is: (object: any) -> boolean,
promisify: <T...>(
callback: (T...) -> ...any
) -> ((T...) -> Promise),
delay: (seconds: number) -> Promise,
retry: <P...>(
callback: (P...) -> Promise,
times: number,
P...
) -> Promise,
retryWithDelay: <P...>(
callback: (P...) -> Promise,
times: number,
seconds: number,
P...
) -> Promise,
fromEvent: (
event: RBXScriptSignal | {Connect: any},
predicate: ((...any) -> boolean)?
) -> Promise,
onUnhandledRejection: (
callback: (Promise, ...any) -> ()
) -> (() -> ())
}
local ERROR_NON_PROMISE_IN_LIST = "Non-promise value passed into %s at index %s"
local ERROR_NON_LIST = "Please pass a list of promises to %s"
local ERROR_NON_FUNCTION = "Please pass a handler function to %s!"
local MODE_KEY_METATABLE = { __mode = "k" }
local MODE_KEY_METATABLE: any = { __mode = "k" }
local function isCallable(value)
local function isCallable(value: any)
if type(value) == "function" then
return true
end
@ -25,7 +156,7 @@ end
--[[
Creates an enum dictionary with some metamethods to prevent common mistakes.
]]
local function makeEnum(enumName, members)
local function makeEnum(enumName: any, members: any)
local enum = {}
for _, memberName in ipairs(members) do
@ -49,7 +180,7 @@ end
@class Error
]=]
local Error
local Error: any
do
Error = {
Kind = makeEnum("Promise.Error.Kind", {
@ -61,7 +192,7 @@ do
}
Error.__index = Error
function Error.new(options, parent)
function Error.new(options: any, parent: any)
options = options or {}
return setmetatable({
error = tostring(options.error) or "[This error has no error text.]",
@ -74,7 +205,7 @@ do
}, Error)
end
function Error.is(anything)
function Error.is(anything: any)
if type(anything) == "table" then
local metatable = getmetatable(anything)
@ -86,13 +217,13 @@ do
return false
end
function Error.isKind(anything, kind)
function Error.isKind(anything: any, kind: any)
assert(kind ~= nil, "Argument #2 to Promise.Error.isKind must not be nil")
return Error.is(anything) and anything.kind == kind
end
function Error:extend(options)
function Error:extend(options: any)
options = options or {}
options.kind = options.kind or self.kind
@ -134,21 +265,21 @@ end
Used to cajole varargs without dropping sparse values.
]]
local function pack(...)
local function pack(...: any)
return select("#", ...), { ... }
end
--[[
Returns first value (success), and packs all following values.
]]
local function packResult(success, ...)
local function packResult(success: any, ...: any)
return success, select("#", ...), { ... }
end
local function makeErrorHandler(traceback)
local function makeErrorHandler(traceback: any)
assert(traceback ~= nil, "traceback is nil")
return function(err)
return function(err: any)
-- If the error object is already a table, forward it directly.
-- Should we extend the error here and add our own trace?
@ -168,7 +299,7 @@ end
--[[
Calls a Promise executor with error handling.
]]
local function runExecutor(traceback, callback, ...)
local function runExecutor(traceback: any, callback: any, ...: any)
return packResult(xpcall(callback, makeErrorHandler(traceback), ...))
end
@ -176,8 +307,8 @@ end
Creates a function that invokes a callback with correct error handling and
resolution mechanisms.
]]
local function createAdvancer(traceback, callback, resolve, reject)
return function(...)
local function createAdvancer(traceback: any, callback: any, resolve: any, reject: any)
return function(...: any)
local ok, resultLength, result = runExecutor(traceback, callback, ...)
if ok then
@ -188,7 +319,7 @@ local function createAdvancer(traceback, callback, resolve, reject)
end
end
local function isEmpty(t)
local function isEmpty(t: any)
return next(t) == nil
end
@ -217,22 +348,23 @@ end
@class Promise
@__index prototype
]=]
local Promise = {
local Promise: any = {
Error = Error,
Status = makeEnum("Promise.Status", { "Started", "Resolved", "Rejected", "Cancelled" }),
_getTime = os.clock,
_timeEvent = game:GetService("RunService").Heartbeat,
_unhandledRejectionCallbacks = {},
TEST = nil :: boolean?,
}
Promise.prototype = {}
Promise.__index = Promise.prototype
function Promise._new(traceback, callback, parent)
function Promise._new(traceback: any, callback: any, parent: any)
if parent ~= nil and not Promise.is(parent) then
error("Argument #2 to Promise.new must be a promise or nil", 2)
end
local self = {
local self: any = {
-- The executor thread.
_thread = nil,
@ -275,15 +407,15 @@ function Promise._new(traceback, callback, parent)
setmetatable(self, Promise)
local function resolve(...)
local function resolve(...: any)
self:_resolve(...)
end
local function reject(...)
local function reject(...: any)
self:_reject(...)
end
local function onCancel(cancellationHook)
local function onCancel(cancellationHook: any)
if cancellationHook then
if self._status == Promise.Status.Cancelled then
cancellationHook()
@ -346,7 +478,7 @@ end
@param executor (resolve: (...: any) -> (), reject: (...: any) -> (), onCancel: (abortHandler?: () -> ()) -> boolean) -> ()
@return Promise
]=]
function Promise.new(executor)
function Promise.new(executor: any)
return Promise._new(debug.traceback(nil, 2), executor)
end
@ -372,7 +504,7 @@ end
@param executor (resolve: (...: any) -> (), reject: (...: any) -> (), onCancel: (abortHandler?: () -> ()) -> boolean) -> ()
@return Promise
]=]
function Promise.defer(executor)
function Promise.defer(executor: any)
local traceback = debug.traceback(nil, 2)
local promise
promise = Promise._new(traceback, function(resolve, reject, onCancel)
@ -415,7 +547,7 @@ Promise.async = Promise.defer
@param ... any
@return Promise<...any>
]=]
function Promise.resolve(...)
function Promise.resolve(...: any)
local length, values = pack(...)
return Promise._new(debug.traceback(nil, 2), function(resolve)
resolve(unpack(values, 1, length))
@ -432,7 +564,7 @@ end
@param ... any
@return Promise<...any>
]=]
function Promise.reject(...)
function Promise.reject(...: any)
local length, values = pack(...)
return Promise._new(debug.traceback(nil, 2), function(_, reject)
reject(unpack(values, 1, length))
@ -443,7 +575,7 @@ end
Runs a non-promise-returning function as a Promise with the
given arguments.
]]
function Promise._try(traceback, callback, ...)
function Promise._try(traceback: any, callback: any, ...: any)
local valuesLength, values = pack(...)
return Promise._new(traceback, function(resolve)
@ -474,7 +606,7 @@ end
@param ... T... -- Additional arguments passed to `callback`
@return Promise
]=]
function Promise.try(callback, ...)
function Promise.try(callback: any, ...: any)
return Promise._try(debug.traceback(nil, 2), callback, ...)
end
@ -483,7 +615,7 @@ end
* is resolved when all input promises resolve
* is rejected if ANY input promises reject
]]
function Promise._all(traceback, promises, amount)
function Promise._all(traceback: any, promises: any, amount: any)
if type(promises) ~= "table" then
error(string.format(ERROR_NON_LIST, "Promise.all"), 3)
end
@ -588,7 +720,7 @@ end
@param promises {Promise<T>}
@return Promise<{T}>
]=]
function Promise.all(promises)
function Promise.all(promises: any)
return Promise._all(debug.traceback(nil, 2), promises)
end
@ -617,7 +749,7 @@ end
@param reducer (accumulator: U, value: T, index: number) -> U | Promise<U>
@param initialValue U
]=]
function Promise.fold(list, reducer, initialValue)
function Promise.fold(list: any, reducer: any, initialValue: any)
assert(type(list) == "table", "Bad argument #1 to Promise.fold: must be a table")
assert(isCallable(reducer), "Bad argument #2 to Promise.fold: must be a function")
@ -650,7 +782,7 @@ end
@param count number
@return Promise<{T}>
]=]
function Promise.some(promises, count)
function Promise.some(promises: any, count: any)
assert(type(count) == "number", "Bad argument #2 to Promise.some: must be a number")
return Promise._all(debug.traceback(nil, 2), promises, count)
@ -674,7 +806,7 @@ end
@param promises {Promise<T>}
@return Promise<T>
]=]
function Promise.any(promises)
function Promise.any(promises: {any})
return Promise._all(debug.traceback(nil, 2), promises, 1):andThen(function(values)
return values[1]
end)
@ -696,7 +828,7 @@ end
@param promises {Promise<T>}
@return Promise<{Status}>
]=]
function Promise.allSettled(promises)
function Promise.allSettled(promises: any)
if type(promises) ~= "table" then
error(string.format(ERROR_NON_LIST, "Promise.allSettled"), 2)
end
@ -774,7 +906,7 @@ end
@param promises {Promise<T>}
@return Promise<T>
]=]
function Promise.race(promises)
function Promise.race(promises: any)
assert(type(promises) == "table", string.format(ERROR_NON_LIST, "Promise.race"))
for i, promise in pairs(promises) do
@ -869,7 +1001,7 @@ end
@param predicate (value: T, index: number) -> U | Promise<U>
@return Promise<{U}>
]=]
function Promise.each(list, predicate)
function Promise.each(list: any, predicate: any)
assert(type(list) == "table", string.format(ERROR_NON_LIST, "Promise.each"))
assert(isCallable(predicate), string.format(ERROR_NON_FUNCTION, "Promise.each"))
@ -959,6 +1091,7 @@ function Promise.each(list, predicate)
end
resolve(results)
return
end)
end
@ -968,7 +1101,7 @@ end
@param object any
@return boolean -- `true` if the given `object` is a Promise.
]=]
function Promise.is(object)
function Promise.is(object: any)
if type(object) ~= "table" then
return false
end
@ -1017,8 +1150,8 @@ end
@param callback (...: any) -> ...any
@return (...: any) -> Promise
]=]
function Promise.promisify(callback)
return function(...)
function Promise.promisify(callback: any)
return function(...: any)
return Promise._try(debug.traceback(nil, 2), callback, ...)
end
end
@ -1048,7 +1181,7 @@ do
local first
local connection
function Promise.delay(seconds)
function Promise.delay(seconds: any)
assert(type(seconds) == "number", "Bad argument #1 to Promise.delay, must be a number.")
-- If seconds is -INF, INF, NaN, or less than 1 / 60, assume seconds is 1 / 60.
-- This mirrors the behavior of wait()
@ -1056,7 +1189,7 @@ do
seconds = 1 / 60
end
return Promise._new(debug.traceback(nil, 2), function(resolve, _, onCancel)
return Promise._new(debug.traceback(nil, 2), function(resolve: any, _: any, onCancel: any)
local startTime = Promise._getTime()
local endTime = startTime + seconds
@ -1177,7 +1310,7 @@ end
@param rejectionValue? any -- The value to reject with if the timeout is reached
@return Promise
]=]
function Promise.prototype:timeout(seconds, rejectionValue)
function Promise.prototype:timeout(seconds: any, rejectionValue: any)
local traceback = debug.traceback(nil, 2)
return Promise.race({
@ -1210,7 +1343,7 @@ end
The given callbacks are invoked depending on that result.
]]
function Promise.prototype:_andThen(traceback, successHandler, failureHandler)
function Promise.prototype:_andThen(traceback: any, successHandler: any, failureHandler: any)
self._unhandledRejection = false
-- If we are already cancelled, we return a cancelled Promise
@ -1222,7 +1355,7 @@ function Promise.prototype:_andThen(traceback, successHandler, failureHandler)
end
-- Create a new promise to follow this part of the chain
return Promise._new(traceback, function(resolve, reject, onCancel)
return Promise._new(traceback, function(resolve: any, reject: any, onCancel: any)
-- Our default callbacks just pass values onto the next promise.
-- This lets success and failure cascade correctly!
@ -1280,7 +1413,7 @@ end
@param failureHandler? (...: any) -> ...any
@return Promise<...any>
]=]
function Promise.prototype:andThen(successHandler, failureHandler)
function Promise.prototype:andThen(successHandler: any, failureHandler: any)
assert(successHandler == nil or isCallable(successHandler), string.format(ERROR_NON_FUNCTION, "Promise:andThen"))
assert(failureHandler == nil or isCallable(failureHandler), string.format(ERROR_NON_FUNCTION, "Promise:andThen"))
@ -1307,7 +1440,7 @@ end
@param failureHandler (...: any) -> ...any
@return Promise<...any>
]=]
function Promise.prototype:catch(failureHandler)
function Promise.prototype:catch(failureHandler: any)
assert(failureHandler == nil or isCallable(failureHandler), string.format(ERROR_NON_FUNCTION, "Promise:catch"))
return self:_andThen(debug.traceback(nil, 2), nil, failureHandler)
end
@ -1328,9 +1461,9 @@ end
@param tapHandler (...: any) -> ...any
@return Promise<...any>
]=]
function Promise.prototype:tap(tapHandler)
function Promise.prototype:tap(tapHandler: any)
assert(isCallable(tapHandler), string.format(ERROR_NON_FUNCTION, "Promise:tap"))
return self:_andThen(debug.traceback(nil, 2), function(...)
return self:_andThen(debug.traceback(nil, 2), function(...: any)
local callbackReturn = tapHandler(...)
if Promise.is(callbackReturn) then
@ -1363,7 +1496,7 @@ end
@param ...? any -- Additional arguments which will be passed to `callback`
@return Promise
]=]
function Promise.prototype:andThenCall(callback, ...)
function Promise.prototype:andThenCall(callback: any, ...: any)
assert(isCallable(callback), string.format(ERROR_NON_FUNCTION, "Promise:andThenCall"))
local length, values = pack(...)
return self:_andThen(debug.traceback(nil, 2), function()
@ -1393,7 +1526,7 @@ end
@param ... any -- Values to return from the function
@return Promise
]=]
function Promise.prototype:andThenReturn(...)
function Promise.prototype:andThenReturn(...: any)
local length, values = pack(...)
return self:_andThen(debug.traceback(nil, 2), function()
return unpack(values, 1, length)
@ -1439,7 +1572,7 @@ end
Used to decrease the number of consumers by 1, and if there are no more,
cancel this promise.
]]
function Promise.prototype:_consumerCancelled(consumer)
function Promise.prototype:_consumerCancelled(consumer: any)
if self._status ~= Promise.Status.Started then
return
end
@ -1455,10 +1588,10 @@ end
Used to set a handler for when the promise resolves, rejects, or is
cancelled.
]]
function Promise.prototype:_finally(traceback, finallyHandler)
function Promise.prototype:_finally(traceback: any, finallyHandler: any)
self._unhandledRejection = false
local promise = Promise._new(traceback, function(resolve, reject, onCancel)
local promise = Promise._new(traceback, function(resolve: any, reject: any, onCancel: any)
local handlerPromise
onCancel(function()
@ -1474,7 +1607,7 @@ function Promise.prototype:_finally(traceback, finallyHandler)
local finallyCallback = resolve
if finallyHandler then
finallyCallback = function(...)
finallyCallback = function(...: any)
local callbackReturn = finallyHandler(...)
if Promise.is(callbackReturn) then
@ -1486,7 +1619,7 @@ function Promise.prototype:_finally(traceback, finallyHandler)
resolve(self)
end
end)
:catch(function(...)
:catch(function(...: any)
reject(...)
end)
else
@ -1556,7 +1689,7 @@ end
@param finallyHandler (status: Status) -> ...any
@return Promise<...any>
]=]
function Promise.prototype:finally(finallyHandler)
function Promise.prototype:finally(finallyHandler: any)
assert(finallyHandler == nil or isCallable(finallyHandler), string.format(ERROR_NON_FUNCTION, "Promise:finally"))
return self:_finally(debug.traceback(nil, 2), finallyHandler)
end
@ -1570,7 +1703,7 @@ end
@param ...? any -- Additional arguments which will be passed to `callback`
@return Promise
]=]
function Promise.prototype:finallyCall(callback, ...)
function Promise.prototype:finallyCall(callback: any, ...: any)
assert(isCallable(callback), string.format(ERROR_NON_FUNCTION, "Promise:finallyCall"))
local length, values = pack(...)
return self:_finally(debug.traceback(nil, 2), function()
@ -1596,7 +1729,7 @@ end
@param ... any -- Values to return from the function
@return Promise
]=]
function Promise.prototype:finallyReturn(...)
function Promise.prototype:finallyReturn(...: any)
local length, values = pack(...)
return self:_finally(debug.traceback(nil, 2), function()
return unpack(values, 1, length)
@ -1638,7 +1771,7 @@ function Promise.prototype:awaitStatus()
return self._status
end
local function awaitHelper(status, ...)
local function awaitHelper(status: any, ...: any)
return status == Promise.Status.Resolved, ...
end
@ -1667,7 +1800,7 @@ function Promise.prototype:await()
return awaitHelper(self:awaitStatus())
end
local function expectHelper(status, ...)
local function expectHelper(status: any, ...: any)
if status ~= Promise.Status.Resolved then
error((...) == nil and "Expected Promise rejected with no value." or (...), 3)
end
@ -1724,7 +1857,7 @@ function Promise.prototype:_unwrap()
return success, unpack(self._values, 1, self._valuesLength)
end
function Promise.prototype:_resolve(...)
function Promise.prototype:_resolve(...: any)
if self._status ~= Promise.Status.Started then
if Promise.is((...)) then
(...):_consumerCancelled(self)
@ -1745,9 +1878,9 @@ function Promise.prototype:_resolve(...)
local chainedPromise = ...
local promise = chainedPromise:andThen(function(...)
local promise = chainedPromise:andThen(function(...: any)
self:_resolve(...)
end, function(...)
end, function(...: any)
local maybeRuntimeError = chainedPromise._values[1]
-- Backwards compatibility < v2
@ -1771,6 +1904,7 @@ function Promise.prototype:_resolve(...)
end
self:_reject(...)
return
end)
if promise._status == Promise.Status.Cancelled then
@ -1795,7 +1929,7 @@ function Promise.prototype:_resolve(...)
self:_finalize()
end
function Promise.prototype:_reject(...)
function Promise.prototype:_reject(...: any)
if self._status ~= Promise.Status.Started then
return
end
@ -1886,10 +2020,10 @@ end
@param rejectionValue? any -- The value to reject with if the Promise isn't resolved
@return Promise
]=]
function Promise.prototype:now(rejectionValue)
function Promise.prototype:now(rejectionValue: any)
local traceback = debug.traceback(nil, 2)
if self._status == Promise.Status.Resolved then
return self:_andThen(traceback, function(...)
return self:_andThen(traceback, function(...: any)
return ...
end)
else
@ -1931,13 +2065,13 @@ end
@param ...? P
@return Promise<T>
]=]
function Promise.retry(callback, times, ...)
function Promise.retry(callback: any, times: any, ...: any)
assert(isCallable(callback), "Parameter #1 to Promise.retry must be a function")
assert(type(times) == "number", "Parameter #2 to Promise.retry must be a number")
local args, length = { ... }, select("#", ...)
return Promise.resolve(callback(...)):catch(function(...)
return Promise.resolve(callback(...)):catch(function(...: any)
if times > 0 then
return Promise.retry(callback, times - 1, unpack(args, 1, length))
else
@ -1959,14 +2093,14 @@ end
@param ...? P
@return Promise<T>
]=]
function Promise.retryWithDelay(callback, times, seconds, ...)
function Promise.retryWithDelay(callback: any, times: any, seconds: any, ...: any)
assert(isCallable(callback), "Parameter #1 to Promise.retry must be a function")
assert(type(times) == "number", "Parameter #2 (times) to Promise.retry must be a number")
assert(type(seconds) == "number", "Parameter #3 (seconds) to Promise.retry must be a number")
local args, length = { ... }, select("#", ...)
return Promise.resolve(callback(...)):catch(function(...)
return Promise.resolve(callback(...)):catch(function(...: any)
if times > 0 then
Promise.delay(seconds):await()
@ -2001,12 +2135,12 @@ end
@param predicate? (...: P) -> boolean -- A function which determines if the Promise should resolve with the given value, or wait for the next event to check again.
@return Promise<P>
]=]
function Promise.fromEvent(event, predicate)
function Promise.fromEvent(event: any, predicate: any)
predicate = predicate or function()
return true
end
return Promise._new(debug.traceback(nil, 2), function(resolve, _, onCancel)
return Promise._new(debug.traceback(nil, 2), function(resolve: any, _: any, onCancel: any)
local connection
local shouldDisconnect = false
@ -2019,8 +2153,8 @@ function Promise.fromEvent(event, predicate)
-- Connect returns, connection will still be nil. This happens with events that queue up
-- events when there's nothing connected, such as RemoteEvents
connection = event:Connect(function(...)
local callbackValue = predicate(...)
connection = event:Connect(function(...: any)
local callbackValue = (predicate :: any)(...)
if callbackValue == true then
resolve(...)
@ -2040,6 +2174,7 @@ function Promise.fromEvent(event, predicate)
end
onCancel(disconnect)
return
end)
end
@ -2053,7 +2188,7 @@ end
@param callback (promise: Promise, ...: any) -- A callback that runs when an unhandled rejection happens.
@return () -> () -- Function that unregisters the `callback` when called
]=]
function Promise.onUnhandledRejection(callback)
function Promise.onUnhandledRejection(callback: any)
table.insert(Promise._unhandledRejectionCallbacks, callback)
return function()
@ -2065,4 +2200,4 @@ function Promise.onUnhandledRejection(callback)
end
end
return Promise
return (Promise :: any) :: PromiseLib

View file

@ -1,8 +0,0 @@
[package]
name = "evaera/roblox-lua-promise"
version = "4.0.0"
author = "evaera"
content_root = "lib"
license = "MIT"
[dependencies]

View file

@ -1 +0,0 @@
std = "roblox+testez"

View file

@ -1,6 +1,6 @@
[package]
name = "evaera/promise"
description = "Promise implementation for Roblox"
name = "ambers-careware/promise"
description = "Promise implementation for Roblox Luau"
version = "4.0.0"
license = "MIT"
registry = "https://github.com/UpliftGames/wally-index"