Guest User

Untitled

a guest
Feb 21st, 2022
211
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.12 KB | None | 0 0
  1. --[[
  2.     An implementation of Promises similar to Promise/A+.
  3. ]]
  4.  
  5. local ERROR_YIELD_NEW = "Yielding inside Promise.new is not allowed! Use Promise.async or create a new thread in the Promise executor!"
  6. local ERROR_YIELD_THEN = "Yielding inside andThen/catch is not allowed! Instead, return a new Promise from andThen/catch."
  7. local ERROR_NON_PROMISE_IN_LIST = "Non-promise value passed into %s at index %s"
  8. local ERROR_NON_LIST = "Please pass a list of promises to %s"
  9. local ERROR_NON_FUNCTION = "Please pass a handler function to %s!"
  10.  
  11. local RunService = game:GetService("RunService")
  12.  
  13. --[[
  14.     Packs a number of arguments into a table and returns its length.
  15.  
  16.     Used to cajole varargs without dropping sparse values.
  17. ]]
  18. local function pack(...)
  19.     local len = select("#", ...)
  20.  
  21.     return len, { ... }
  22. end
  23.  
  24. --[[
  25.     Returns first value (success), and packs all following values.
  26. ]]
  27. local function packResult(...)
  28.     local result = (...)
  29.  
  30.     return result, pack(select(2, ...))
  31. end
  32.  
  33. --[[
  34.     Calls a non-yielding function in a new coroutine.
  35.  
  36.     Handles errors if they happen.
  37. ]]
  38. local function ppcall(yieldError, callback, ...)
  39.     -- Wrapped because C functions can't be passed to coroutine.create!
  40.     local co = coroutine.create(function(...)
  41.         return callback(...)
  42.     end)
  43.  
  44.     local ok, len, result = packResult(coroutine.resume(co, ...))
  45.  
  46.     if ok and coroutine.status(co) ~= "dead" then
  47.         error(yieldError, 2)
  48.     end
  49.  
  50.     return ok, len, result
  51. end
  52.  
  53. --[[
  54.     Creates a function that invokes a callback with correct error handling and
  55.     resolution mechanisms.
  56. ]]
  57. local function createAdvancer(traceback, callback, resolve, reject)
  58.     return function(...)
  59.         local ok, resultLength, result = ppcall(ERROR_YIELD_THEN, callback, ...)
  60.  
  61.         if ok then
  62.             resolve(unpack(result, 1, resultLength))
  63.         else
  64.             reject(result[1], traceback)
  65.         end
  66.     end
  67. end
  68.  
  69. local function isEmpty(t)
  70.     return next(t) == nil
  71. end
  72.  
  73. local Promise = {}
  74. Promise.prototype = {}
  75. Promise.__index = Promise.prototype
  76.  
  77. Promise.Status = setmetatable({
  78.     Started = "Started",
  79.     Resolved = "Resolved",
  80.     Rejected = "Rejected",
  81.     Cancelled = "Cancelled",
  82. }, {
  83.     __index = function(_, k)
  84.         error(("%s is not in Promise.Status!"):format(k), 2)
  85.     end
  86. })
  87.  
  88. --[[
  89.     Constructs a new Promise with the given initializing callback.
  90.  
  91.     This is generally only called when directly wrapping a non-promise API into
  92.     a promise-based version.
  93.  
  94.     The callback will receive 'resolve' and 'reject' methods, used to start
  95.     invoking the promise chain.
  96.  
  97.     Second parameter, parent, is used internally for tracking the "parent" in a
  98.     promise chain. External code shouldn't need to worry about this.
  99. ]]
  100. function Promise.new(callback, parent)
  101.     if parent ~= nil and not Promise.is(parent) then
  102.         error("Argument #2 to Promise.new must be a promise or nil", 2)
  103.     end
  104.  
  105.     local self = {
  106.         -- Used to locate where a promise was created
  107.         _source = debug.traceback(),
  108.  
  109.         _status = Promise.Status.Started,
  110.  
  111.         -- Will be set to the Lua error string if it occurs while executing.
  112.         _error = nil,
  113.  
  114.         -- A table containing a list of all results, whether success or failure.
  115.         -- Only valid if _status is set to something besides Started
  116.         _values = nil,
  117.  
  118.         -- Lua doesn't like sparse arrays very much, so we explicitly store the
  119.         -- length of _values to handle middle nils.
  120.         _valuesLength = -1,
  121.  
  122.         -- Tracks if this Promise has no error observers..
  123.         _unhandledRejection = true,
  124.  
  125.         -- Queues representing functions we should invoke when we update!
  126.         _queuedResolve = {},
  127.         _queuedReject = {},
  128.         _queuedFinally = {},
  129.  
  130.         -- The function to run when/if this promise is cancelled.
  131.         _cancellationHook = nil,
  132.  
  133.         -- The "parent" of this promise in a promise chain. Required for
  134.         -- cancellation propagation.
  135.         _parent = parent,
  136.  
  137.         _consumers = setmetatable({}, {
  138.             __mode = "k";
  139.         }),
  140.     }
  141.  
  142.     if parent and parent._status == Promise.Status.Started then
  143.         parent._consumers[self] = true
  144.     end
  145.  
  146.     setmetatable(self, Promise)
  147.  
  148.     local function resolve(...)
  149.         self:_resolve(...)
  150.     end
  151.  
  152.     local function reject(...)
  153.         self:_reject(...)
  154.     end
  155.  
  156.     local function onCancel(cancellationHook)
  157.         if cancellationHook then
  158.             if self._status == Promise.Status.Cancelled then
  159.                 cancellationHook()
  160.             else
  161.                 self._cancellationHook = cancellationHook
  162.             end
  163.         end
  164.  
  165.         return self._status == Promise.Status.Cancelled
  166.     end
  167.  
  168.     local ok, _, result = ppcall(
  169.         ERROR_YIELD_NEW,
  170.         callback,
  171.         resolve,
  172.         reject,
  173.         onCancel
  174.     )
  175.  
  176.     if not ok then
  177.         self._error = result[1] or "error"
  178.         reject((result[1] or "error") .. "\n" .. self._source)
  179.     end
  180.  
  181.     return self
  182. end
  183.  
  184. function Promise._newWithSelf(executor, ...)
  185.     local args
  186.     local promise = Promise.new(function(...)
  187.         args = {...}
  188.     end, ...)
  189.  
  190.     executor(promise, unpack(args))
  191.  
  192.     return promise
  193. end
  194.  
  195. function Promise._new(traceback, executor, ...)
  196.     return Promise._newWithSelf(function(self, resolve, reject)
  197.         self._source = traceback
  198.  
  199.         executor(resolve, function(err, traceback)
  200.             err = err or "error"
  201.             traceback = traceback or ""
  202.             self._error = err
  203.             reject(err .. "\n" .. traceback)
  204.         end)
  205.     end, ...)
  206. end
  207.  
  208. --[[
  209.     Promise.new, except pcall on a new thread is automatic.
  210. ]]
  211. function Promise.async(callback)
  212.     local traceback = debug.traceback()
  213.     local promise
  214.     promise = Promise.new(function(resolve, reject, onCancel)
  215.         local connection
  216.         connection = RunService.Heartbeat:Connect(function()
  217.             connection:Disconnect()
  218.             local ok, err = pcall(callback, resolve, reject, onCancel)
  219.  
  220.             if not ok then
  221.                 promise._error = err or "error"
  222.                 reject(err .. "\n" .. traceback)
  223.             end
  224.         end)
  225.     end)
  226.  
  227.     return promise
  228. end
  229.  
  230. --[[
  231.     Create a promise that represents the immediately resolved value.
  232. ]]
  233. function Promise.resolve(...)
  234.     local length, values = pack(...)
  235.     return Promise.new(function(resolve)
  236.         resolve(unpack(values, 1, length))
  237.     end)
  238. end
  239.  
  240. --[[
  241.     Create a promise that represents the immediately rejected value.
  242. ]]
  243. function Promise.reject(...)
  244.     local length, values = pack(...)
  245.     return Promise.new(function(_, reject)
  246.         reject(unpack(values, 1, length))
  247.     end)
  248. end
  249.  
  250. --[[
  251.     Begins a Promise chain, turning synchronous errors into rejections.
  252. ]]
  253. function Promise.try(...)
  254.     return Promise.resolve():andThenCall(...)
  255. end
  256.  
  257. --[[
  258.     Returns a new promise that:
  259.         * is resolved when all input promises resolve
  260.         * is rejected if ANY input promises reject
  261. ]]
  262. function Promise._all(traceback, promises, amount)
  263.     if type(promises) ~= "table" then
  264.         error(ERROR_NON_LIST:format("Promise.all"), 3)
  265.     end
  266.  
  267.     -- We need to check that each value is a promise here so that we can produce
  268.     -- a proper error rather than a rejected promise with our error.
  269.     for i, promise in pairs(promises) do
  270.         if not Promise.is(promise) then
  271.             error((ERROR_NON_PROMISE_IN_LIST):format("Promise.all", tostring(i)), 3)
  272.         end
  273.     end
  274.  
  275.     -- If there are no values then return an already resolved promise.
  276.     if #promises == 0 or amount == 0 then
  277.         return Promise.resolve({})
  278.     end
  279.  
  280.     return Promise._newWithSelf(function(self, resolve, reject, onCancel)
  281.         self._source = traceback
  282.  
  283.         -- An array to contain our resolved values from the given promises.
  284.         local resolvedValues = {}
  285.         local newPromises = {}
  286.  
  287.         -- Keep a count of resolved promises because just checking the resolved
  288.         -- values length wouldn't account for promises that resolve with nil.
  289.         local resolvedCount = 0
  290.         local rejectedCount = 0
  291.         local done = false
  292.  
  293.         local function cancel()
  294.             for _, promise in ipairs(newPromises) do
  295.                 promise:cancel()
  296.             end
  297.         end
  298.  
  299.         -- Called when a single value is resolved and resolves if all are done.
  300.         local function resolveOne(i, ...)
  301.             if done then
  302.                 return
  303.             end
  304.  
  305.             resolvedCount = resolvedCount + 1
  306.  
  307.             if amount == nil then
  308.                 resolvedValues[i] = ...
  309.             else
  310.                 resolvedValues[resolvedCount] = ...
  311.             end
  312.  
  313.             if resolvedCount >= (amount or #promises) then
  314.                 done = true
  315.                 resolve(resolvedValues)
  316.                 cancel()
  317.             end
  318.         end
  319.  
  320.         onCancel(cancel)
  321.  
  322.         -- We can assume the values inside `promises` are all promises since we
  323.         -- checked above.
  324.         for i = 1, #promises do
  325.             table.insert(
  326.                 newPromises,
  327.                 promises[i]:andThen(
  328.                     function(...)
  329.                         resolveOne(i, ...)
  330.                     end,
  331.                     function(...)
  332.                         rejectedCount = rejectedCount + 1
  333.  
  334.                         if amount == nil or #promises - rejectedCount < amount then
  335.                             cancel()
  336.                             done = true
  337.  
  338.                             reject(...)
  339.                         end
  340.                     end
  341.                 )
  342.             )
  343.         end
  344.  
  345.         if done then
  346.             cancel()
  347.         end
  348.     end)
  349. end
  350.  
  351. function Promise.all(promises)
  352.     return Promise._all(debug.traceback(), promises)
  353. end
  354.  
  355. function Promise.some(promises, amount)
  356.     assert(type(amount) == "number", "Bad argument #2 to Promise.some: must be a number")
  357.  
  358.     return Promise._all(debug.traceback(), promises, amount)
  359. end
  360.  
  361. function Promise.any(promises)
  362.     return Promise._all(debug.traceback(), promises, 1):andThen(function(values)
  363.         return values[1]
  364.     end)
  365. end
  366.  
  367. function Promise.allSettled(promises)
  368.     if type(promises) ~= "table" then
  369.         error(ERROR_NON_LIST:format("Promise.allSettled"), 2)
  370.     end
  371.  
  372.     -- We need to check that each value is a promise here so that we can produce
  373.     -- a proper error rather than a rejected promise with our error.
  374.     for i, promise in pairs(promises) do
  375.         if not Promise.is(promise) then
  376.             error((ERROR_NON_PROMISE_IN_LIST):format("Promise.allSettled", tostring(i)), 2)
  377.         end
  378.     end
  379.  
  380.     -- If there are no values then return an already resolved promise.
  381.     if #promises == 0 then
  382.         return Promise.resolve({})
  383.     end
  384.  
  385.     return Promise.new(function(resolve, _, onCancel)
  386.         -- An array to contain our resolved values from the given promises.
  387.         local fates = {}
  388.         local newPromises = {}
  389.  
  390.         -- Keep a count of resolved promises because just checking the resolved
  391.         -- values length wouldn't account for promises that resolve with nil.
  392.         local finishedCount = 0
  393.  
  394.         -- Called when a single value is resolved and resolves if all are done.
  395.         local function resolveOne(i, ...)
  396.             finishedCount = finishedCount + 1
  397.  
  398.             fates[i] = ...
  399.  
  400.             if finishedCount >= #promises then
  401.                 resolve(fates)
  402.             end
  403.         end
  404.  
  405.         onCancel(function()
  406.             for _, promise in ipairs(newPromises) do
  407.                 promise:cancel()
  408.             end
  409.         end)
  410.  
  411.         -- We can assume the values inside `promises` are all promises since we
  412.         -- checked above.
  413.         for i = 1, #promises do
  414.             table.insert(
  415.                 newPromises,
  416.                 promises[i]:finally(
  417.                     function(...)
  418.                         resolveOne(i, ...)
  419.                     end
  420.                 )
  421.             )
  422.         end
  423.     end)
  424. end
  425.  
  426. --[[
  427.     Races a set of Promises and returns the first one that resolves,
  428.     cancelling the others.
  429. ]]
  430. function Promise.race(promises)
  431.     assert(type(promises) == "table", ERROR_NON_LIST:format("Promise.race"))
  432.  
  433.     for i, promise in pairs(promises) do
  434.         assert(Promise.is(promise), (ERROR_NON_PROMISE_IN_LIST):format("Promise.race", tostring(i)))
  435.     end
  436.  
  437.     return Promise.new(function(resolve, reject, onCancel)
  438.         local newPromises = {}
  439.         local finished = false
  440.  
  441.         local function cancel()
  442.             for _, promise in ipairs(newPromises) do
  443.                 promise:cancel()
  444.             end
  445.         end
  446.  
  447.         local function finalize(callback)
  448.             return function (...)
  449.                 cancel()
  450.                 finished = true
  451.                 return callback(...)
  452.             end
  453.         end
  454.  
  455.         if onCancel(finalize(reject)) then
  456.             return
  457.         end
  458.  
  459.         for _, promise in ipairs(promises) do
  460.             table.insert(
  461.                 newPromises,
  462.                 promise:andThen(finalize(resolve), finalize(reject))
  463.             )
  464.         end
  465.  
  466.         if finished then
  467.             cancel()
  468.         end
  469.     end)
  470. end
  471.  
  472. --[[
  473.     Is the given object a Promise instance?
  474. ]]
  475. function Promise.is(object)
  476.     if type(object) ~= "table" then
  477.         return false
  478.     end
  479.  
  480.     return type(object.andThen) == "function"
  481. end
  482.  
  483. --[[
  484.     Converts a yielding function into a Promise-returning one.
  485. ]]
  486. function Promise.promisify(callback)
  487.     return function(...)
  488.         local traceback = debug.traceback()
  489.         local length, values = pack(...)
  490.         return Promise.new(function(resolve, reject)
  491.             coroutine.wrap(function()
  492.                 local ok, resultLength, resultValues = packResult(pcall(callback, unpack(values, 1, length)))
  493.                 if ok then
  494.                     resolve(unpack(resultValues, 1, resultLength))
  495.                 else
  496.                     reject((resultValues[1] or "error") .. "\n" .. traceback)
  497.                 end
  498.             end)()
  499.         end)
  500.     end
  501. end
  502.  
  503. --[[
  504.     Creates a Promise that resolves after given number of seconds.
  505. ]]
  506. do
  507.     local connection
  508.     local queue = {}
  509.  
  510.     local function enqueue(callback, seconds)
  511.         table.insert(queue, {
  512.             callback = callback,
  513.             startTime = tick(),
  514.             endTime = tick() + math.max(seconds, 1/60)
  515.         })
  516.  
  517.         table.sort(queue, function(a, b)
  518.             return a.endTime < b.endTime
  519.         end)
  520.  
  521.         if not connection then
  522.             connection = RunService.Heartbeat:Connect(function()
  523.                 while #queue > 0 and queue[1].endTime <= tick() do
  524.                     local item = table.remove(queue, 1)
  525.  
  526.                     item.callback(tick() - item.startTime)
  527.                 end
  528.  
  529.                 if #queue == 0 then
  530.                     connection:Disconnect()
  531.                     connection = nil
  532.                 end
  533.             end)
  534.         end
  535.     end
  536.  
  537.     local function dequeue(callback)
  538.         for i, item in ipairs(queue) do
  539.             if item.callback == callback then
  540.                 table.remove(queue, i)
  541.                 break
  542.             end
  543.         end
  544.     end
  545.  
  546.     function Promise.delay(seconds)
  547.         assert(type(seconds) == "number", "Bad argument #1 to Promise.delay, must be a number.")
  548.         -- If seconds is -INF, INF, or NaN, assume seconds is 0.
  549.         -- This mirrors the behavior of wait()
  550.         if seconds < 0 or seconds == math.huge or seconds ~= seconds then
  551.             seconds = 0
  552.         end
  553.  
  554.         return Promise.new(function(resolve, _, onCancel)
  555.             enqueue(resolve, seconds)
  556.  
  557.             onCancel(function()
  558.                 dequeue(resolve)
  559.             end)
  560.         end)
  561.     end
  562. end
  563.  
  564. --[[
  565.     Rejects the promise after `seconds` seconds.
  566. ]]
  567. function Promise.prototype:timeout(seconds, timeoutValue)
  568.     return Promise.race({
  569.         Promise.delay(seconds):andThen(function()
  570.             return Promise.reject(timeoutValue == nil and "Timed out" or timeoutValue)
  571.         end),
  572.         self
  573.     })
  574. end
  575.  
  576. function Promise.prototype:getStatus()
  577.     return self._status
  578. end
  579.  
  580. --[[
  581.     Creates a new promise that receives the result of this promise.
  582.  
  583.     The given callbacks are invoked depending on that result.
  584. ]]
  585. function Promise.prototype:_andThen(traceback, successHandler, failureHandler)
  586.     self._unhandledRejection = false
  587.  
  588.     -- Create a new promise to follow this part of the chain
  589.     return Promise._new(traceback, function(resolve, reject)
  590.         -- Our default callbacks just pass values onto the next promise.
  591.         -- This lets success and failure cascade correctly!
  592.  
  593.         local successCallback = resolve
  594.         if successHandler then
  595.             successCallback = createAdvancer(
  596.                 traceback,
  597.                 successHandler,
  598.                 resolve,
  599.                 reject
  600.             )
  601.         end
  602.  
  603.         local failureCallback = reject
  604.         if failureHandler then
  605.             failureCallback = createAdvancer(
  606.                 traceback,
  607.                 failureHandler,
  608.                 resolve,
  609.                 reject
  610.             )
  611.         end
  612.  
  613.         if self._status == Promise.Status.Started then
  614.             -- If we haven't resolved yet, put ourselves into the queue
  615.             table.insert(self._queuedResolve, successCallback)
  616.             table.insert(self._queuedReject, failureCallback)
  617.         elseif self._status == Promise.Status.Resolved then
  618.             -- This promise has already resolved! Trigger success immediately.
  619.             successCallback(unpack(self._values, 1, self._valuesLength))
  620.         elseif self._status == Promise.Status.Rejected then
  621.             -- This promise died a terrible death! Trigger failure immediately.
  622.             failureCallback(unpack(self._values, 1, self._valuesLength))
  623.         elseif self._status == Promise.Status.Cancelled then
  624.             -- We don't want to call the success handler or the failure handler,
  625.             -- we just reject this promise outright.
  626.             reject("Promise is cancelled")
  627.         end
  628.     end, self)
  629. end
  630.  
  631. function Promise.prototype:andThen(successHandler, failureHandler)
  632.     assert(
  633.         successHandler == nil or type(successHandler) == "function",
  634.         ERROR_NON_FUNCTION:format("Promise:andThen")
  635.     )
  636.     assert(
  637.         failureHandler == nil or type(failureHandler) == "function",
  638.         ERROR_NON_FUNCTION:format("Promise:andThen")
  639.     )
  640.  
  641.     return self:_andThen(debug.traceback(), successHandler, failureHandler)
  642. end
  643.  
  644. --[[
  645.     Used to catch any errors that may have occurred in the promise.
  646. ]]
  647. function Promise.prototype:catch(failureCallback)
  648.     assert(
  649.         failureCallback == nil or type(failureCallback) == "function",
  650.         ERROR_NON_FUNCTION:format("Promise:catch")
  651.     )
  652.     return self:_andThen(debug.traceback(), nil, failureCallback)
  653. end
  654.  
  655. --[[
  656.     Like andThen, but the value passed into the handler is also the
  657.     value returned from the handler.
  658. ]]
  659. function Promise.prototype:tap(tapCallback)
  660.     assert(type(tapCallback) == "function", ERROR_NON_FUNCTION:format("Promise:tap"))
  661.     return self:_andThen(debug.traceback(), function(...)
  662.         local callbackReturn = tapCallback(...)
  663.  
  664.         if Promise.is(callbackReturn) then
  665.             local length, values = pack(...)
  666.             return callbackReturn:andThen(function()
  667.                 return unpack(values, 1, length)
  668.             end)
  669.         end
  670.  
  671.         return ...
  672.     end)
  673. end
  674.  
  675. --[[
  676.     Calls a callback on `andThen` with specific arguments.
  677. ]]
  678. function Promise.prototype:andThenCall(callback, ...)
  679.     assert(type(callback) == "function", ERROR_NON_FUNCTION:format("Promise:andThenCall"))
  680.     local length, values = pack(...)
  681.     return self:_andThen(debug.traceback(), function()
  682.         return callback(unpack(values, 1, length))
  683.     end)
  684. end
  685.  
  686. --[[
  687.     Shorthand for an andThen handler that returns the given value.
  688. ]]
  689. function Promise.prototype:andThenReturn(...)
  690.     local length, values = pack(...)
  691.     return self:_andThen(debug.traceback(), function()
  692.         return unpack(values, 1, length)
  693.     end)
  694. end
  695.  
  696. --[[
  697.     Cancels the promise, disallowing it from rejecting or resolving, and calls
  698.     the cancellation hook if provided.
  699. ]]
  700. function Promise.prototype:cancel()
  701.     if self._status ~= Promise.Status.Started then
  702.         return
  703.     end
  704.  
  705.     self._status = Promise.Status.Cancelled
  706.  
  707.     if self._cancellationHook then
  708.         self._cancellationHook()
  709.     end
  710.  
  711.     if self._parent then
  712.         self._parent:_consumerCancelled(self)
  713.     end
  714.  
  715.     for child in pairs(self._consumers) do
  716.         child:cancel()
  717.     end
  718.  
  719.     self:_finalize()
  720. end
  721.  
  722. --[[
  723.     Used to decrease the number of consumers by 1, and if there are no more,
  724.     cancel this promise.
  725. ]]
  726. function Promise.prototype:_consumerCancelled(consumer)
  727.     if self._status ~= Promise.Status.Started then
  728.         return
  729.     end
  730.  
  731.     self._consumers[consumer] = nil
  732.  
  733.     if next(self._consumers) == nil then
  734.         self:cancel()
  735.     end
  736. end
  737.  
  738. --[[
  739.     Used to set a handler for when the promise resolves, rejects, or is
  740.     cancelled. Returns a new promise chained from this promise.
  741. ]]
  742. function Promise.prototype:_finally(traceback, finallyHandler, onlyOk)
  743.     if not onlyOk then
  744.         self._unhandledRejection = false
  745.     end
  746.  
  747.     -- Return a promise chained off of this promise
  748.     return Promise._new(traceback, function(resolve, reject)
  749.         local finallyCallback = resolve
  750.         if finallyHandler then
  751.             finallyCallback = createAdvancer(
  752.                 traceback,
  753.                 finallyHandler,
  754.                 resolve,
  755.                 reject
  756.             )
  757.         end
  758.  
  759.         if onlyOk then
  760.             local callback = finallyCallback
  761.             finallyCallback = function(...)
  762.                 if self._status == Promise.Status.Rejected then
  763.                     return resolve(self)
  764.                 end
  765.  
  766.                 return callback(...)
  767.             end
  768.         end
  769.  
  770.         if self._status == Promise.Status.Started then
  771.             -- The promise is not settled, so queue this.
  772.             table.insert(self._queuedFinally, finallyCallback)
  773.         else
  774.             -- The promise already settled or was cancelled, run the callback now.
  775.             finallyCallback(self._status)
  776.         end
  777.     end, self)
  778. end
  779.  
  780. function Promise.prototype:finally(finallyHandler)
  781.     assert(
  782.         finallyHandler == nil or type(finallyHandler) == "function",
  783.         ERROR_NON_FUNCTION:format("Promise:finally")
  784.     )
  785.     return self:_finally(debug.traceback(), finallyHandler)
  786. end
  787.  
  788. --[[
  789.     Calls a callback on `finally` with specific arguments.
  790. ]]
  791. function Promise.prototype:finallyCall(callback, ...)
  792.     assert(type(callback) == "function", ERROR_NON_FUNCTION:format("Promise:finallyCall"))
  793.     local length, values = pack(...)
  794.     return self:_finally(debug.traceback(), function()
  795.         return callback(unpack(values, 1, length))
  796.     end)
  797. end
  798.  
  799. --[[
  800.     Shorthand for a finally handler that returns the given value.
  801. ]]
  802. function Promise.prototype:finallyReturn(...)
  803.     local length, values = pack(...)
  804.     return self:_finally(debug.traceback(), function()
  805.         return unpack(values, 1, length)
  806.     end)
  807. end
  808.  
  809. --[[
  810.     Similar to finally, except rejections are propagated through it.
  811. ]]
  812. function Promise.prototype:done(finallyHandler)
  813.     assert(
  814.         finallyHandler == nil or type(finallyHandler) == "function",
  815.         ERROR_NON_FUNCTION:format("Promise:finallyO")
  816.     )
  817.     return self:_finally(debug.traceback(), finallyHandler, true)
  818. end
  819.  
  820. --[[
  821.     Calls a callback on `done` with specific arguments.
  822. ]]
  823. function Promise.prototype:doneCall(callback, ...)
  824.     assert(type(callback) == "function", ERROR_NON_FUNCTION:format("Promise:doneCall"))
  825.     local length, values = pack(...)
  826.     return self:_finally(debug.traceback(), function()
  827.         return callback(unpack(values, 1, length))
  828.     end, true)
  829. end
  830.  
  831. --[[
  832.     Shorthand for a done handler that returns the given value.
  833. ]]
  834. function Promise.prototype:doneReturn(...)
  835.     local length, values = pack(...)
  836.     return self:_finally(debug.traceback(), function()
  837.         return unpack(values, 1, length)
  838.     end, true)
  839. end
  840.  
  841. --[[
  842.     Yield until the promise is completed.
  843.  
  844.     This matches the execution model of normal Roblox functions.
  845. ]]
  846. function Promise.prototype:awaitStatus()
  847.     self._unhandledRejection = false
  848.  
  849.     if self._status == Promise.Status.Started then
  850.         local bindable = Instance.new("BindableEvent")
  851.  
  852.         self:finally(function()
  853.             bindable:Fire()
  854.         end)
  855.  
  856.         bindable.Event:Wait()
  857.         bindable:Destroy()
  858.     end
  859.  
  860.     if self._status == Promise.Status.Resolved then
  861.         return self._status, unpack(self._values, 1, self._valuesLength)
  862.     elseif self._status == Promise.Status.Rejected then
  863.         return self._status, unpack(self._values, 1, self._valuesLength)
  864.     end
  865.  
  866.     return self._status
  867. end
  868.  
  869. --[[
  870.     Calls awaitStatus internally, returns (isResolved, values...)
  871. ]]
  872. function Promise.prototype:await(...)
  873.     local length, result = pack(self:awaitStatus(...))
  874.     local status = table.remove(result, 1)
  875.  
  876.     return status == Promise.Status.Resolved, unpack(result, 1, length - 1)
  877. end
  878.  
  879. --[[
  880.     Calls await and only returns if the Promise resolves.
  881.     Throws if the Promise rejects or gets cancelled.
  882. ]]
  883. function Promise.prototype:awaitValue(...)
  884.     local length, result = pack(self:awaitStatus(...))
  885.     local status = table.remove(result, 1)
  886.  
  887.     assert(
  888.         status == Promise.Status.Resolved,
  889.         tostring(result[1] == nil and "" or result[1])
  890.     )
  891.  
  892.     return unpack(result, 1, length - 1)
  893. end
  894.  
  895. --[[
  896.     Intended for use in tests.
  897.  
  898.     Similar to await(), but instead of yielding if the promise is unresolved,
  899.     _unwrap will throw. This indicates an assumption that a promise has
  900.     resolved.
  901. ]]
  902. function Promise.prototype:_unwrap()
  903.     if self._status == Promise.Status.Started then
  904.         error("Promise has not resolved or rejected.", 2)
  905.     end
  906.  
  907.     local success = self._status == Promise.Status.Resolved
  908.  
  909.     return success, unpack(self._values, 1, self._valuesLength)
  910. end
  911.  
  912. function Promise.prototype:_resolve(...)
  913.     if self._status ~= Promise.Status.Started then
  914.         if Promise.is((...)) then
  915.             (...):_consumerCancelled(self)
  916.         end
  917.         return
  918.     end
  919.  
  920.     -- If the resolved value was a Promise, we chain onto it!
  921.     if Promise.is((...)) then
  922.         -- Without this warning, arguments sometimes mysteriously disappear
  923.         if select("#", ...) > 1 then
  924.             local message = (
  925.                 "When returning a Promise from andThen, extra arguments are " ..
  926.                 "discarded! See:\n\n%s"
  927.             ):format(
  928.                 self._source
  929.             )
  930.             warn(message)
  931.         end
  932.  
  933.         local chainedPromise = ...
  934.  
  935.         local promise = chainedPromise:andThen(
  936.             function(...)
  937.                 self:_resolve(...)
  938.             end,
  939.             function(...)
  940.                 -- The handler errored. Replace the inner stack trace with our outer stack trace.
  941.                 if chainedPromise._error then
  942.                     return self:_reject((chainedPromise._error or "") .. "\n" .. self._source)
  943.                 end
  944.                 self:_reject(...)
  945.             end
  946.         )
  947.  
  948.         if promise._status == Promise.Status.Cancelled then
  949.             self:cancel()
  950.         elseif promise._status == Promise.Status.Started then
  951.             -- Adopt ourselves into promise for cancellation propagation.
  952.             self._parent = promise
  953.             promise._consumers[self] = true
  954.         end
  955.  
  956.         return
  957.     end
  958.  
  959.     self._status = Promise.Status.Resolved
  960.     self._valuesLength, self._values = pack(...)
  961.  
  962.     -- We assume that these callbacks will not throw errors.
  963.     for _, callback in ipairs(self._queuedResolve) do
  964.         callback(...)
  965.     end
  966.  
  967.     self:_finalize()
  968. end
  969.  
  970. function Promise.prototype:_reject(...)
  971.     if self._status ~= Promise.Status.Started then
  972.         return
  973.     end
  974.  
  975.     self._status = Promise.Status.Rejected
  976.     self._valuesLength, self._values = pack(...)
  977.  
  978.     -- If there are any rejection handlers, call those!
  979.     if not isEmpty(self._queuedReject) then
  980.         -- We assume that these callbacks will not throw errors.
  981.         for _, callback in ipairs(self._queuedReject) do
  982.             callback(...)
  983.         end
  984.     else
  985.         -- At this point, no one was able to observe the error.
  986.         -- An error handler might still be attached if the error occurred
  987.         -- synchronously. We'll wait one tick, and if there are still no
  988.         -- observers, then we should put a message in the console.
  989.  
  990.         local err = tostring((...))
  991.  
  992.         coroutine.wrap(function()
  993.             RunService.Heartbeat:Wait()
  994.  
  995.             -- Someone observed the error, hooray!
  996.             if not self._unhandledRejection then
  997.                 return
  998.             end
  999.  
  1000.             -- Build a reasonable message
  1001.             local message
  1002.             if self._error then
  1003.                 message = ("Unhandled promise rejection:\n\n%s"):format(err)
  1004.             else
  1005.                 message = ("Unhandled promise rejection:\n\n%s\n\n%s"):format(
  1006.                     err,
  1007.                     self._source
  1008.                 )
  1009.             end
  1010.             warn(message)
  1011.         end)()
  1012.     end
  1013.  
  1014.     self:_finalize()
  1015. end
  1016.  
  1017. --[[
  1018.     Calls any :finally handlers. We need this to be a separate method and
  1019.     queue because we must call all of the finally callbacks upon a success,
  1020.     failure, *and* cancellation.
  1021. ]]
  1022. function Promise.prototype:_finalize()
  1023.     for _, callback in ipairs(self._queuedFinally) do
  1024.         -- Purposefully not passing values to callbacks here, as it could be the
  1025.         -- resolved values, or rejected errors. If the developer needs the values,
  1026.         -- they should use :andThen or :catch explicitly.
  1027.         callback(self._status)
  1028.     end
  1029.  
  1030.     if self._parent and self._error == nil then
  1031.         self._error = self._parent._error
  1032.     end
  1033.  
  1034.     -- Allow family to be buried
  1035.     if not Promise.TEST then
  1036.         self._parent = nil
  1037.         self._consumers = nil
  1038.     end
  1039. end
  1040.  
  1041. return Promise
Advertisement
Add Comment
Please, Sign In to add comment