mirror of
https://github.com/AmberGraceRblx/luau-promise.git
synced 2025-04-24 23:50:03 +00:00
Implement Promise.all (#4)
This commit is contained in:
parent
e6b43a1fae
commit
371f08598c
3 changed files with 111 additions and 4 deletions
|
@ -25,6 +25,11 @@ This Promise implementation attempts to satisfy those traits.
|
||||||
* Creates an immediately rejected Promise with the given value.
|
* Creates an immediately rejected Promise with the given value.
|
||||||
* `Promise.is(object) -> bool`
|
* `Promise.is(object) -> bool`
|
||||||
* Returns whether the given object is a Promise.
|
* Returns whether the given object is a Promise.
|
||||||
|
* `Promise.all(array) -> array`
|
||||||
|
* Accepts an array of promises and returns a new promise that:
|
||||||
|
* is resolved after all input promises resolve.
|
||||||
|
* is rejected if ANY input promises reject.
|
||||||
|
* Note: Only the first return value from each promise will be present in the resulting array.
|
||||||
|
|
||||||
### Instance Methods
|
### Instance Methods
|
||||||
* `Promise:andThen(successHandler, [failureHandler]) -> Promise`
|
* `Promise:andThen(successHandler, [failureHandler]) -> Promise`
|
||||||
|
@ -69,8 +74,6 @@ httpGet("https://google.com")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Future Additions
|
## Future Additions
|
||||||
* `Promise.all`
|
|
||||||
* Currently stubbed out, throws an error.
|
|
||||||
* `Promise.wrapAsync`
|
* `Promise.wrapAsync`
|
||||||
* Intended to wrap an existing Roblox API that yields, exposing a new one that returns a Promise.
|
* Intended to wrap an existing Roblox API that yields, exposing a new one that returns a Promise.
|
||||||
|
|
||||||
|
|
56
lib/init.lua
56
lib/init.lua
|
@ -165,8 +165,60 @@ end
|
||||||
* is resolved when all input promises resolve
|
* is resolved when all input promises resolve
|
||||||
* is rejected if ANY input promises reject
|
* is rejected if ANY input promises reject
|
||||||
]]
|
]]
|
||||||
function Promise.all(...)
|
function Promise.all(promises)
|
||||||
error("unimplemented", 2)
|
if type(promises) ~= "table" then
|
||||||
|
error("Please pass an array of promises or values to Promise.all", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If there are no values then return an already resolved promise.
|
||||||
|
if #promises == 0 then
|
||||||
|
return Promise.resolve({})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We need to check that each value is a promise here so that we can produce a
|
||||||
|
-- proper error rather than a rejected promise with our error.
|
||||||
|
for i = 1, #promises do
|
||||||
|
if not Promise.is(promises[i]) then
|
||||||
|
error(("Non-promise value passed into Promise.all at index #%d"):format(i), 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Promise.new(function(resolve, reject)
|
||||||
|
-- An array to contain our resolved values from the given promises.
|
||||||
|
local resolvedValues = {}
|
||||||
|
-- Keep a count of resolved promises because just checking the resolved values length
|
||||||
|
-- wouldn't account for promises that resolve with nil.
|
||||||
|
local resolvedCount = 0
|
||||||
|
local rejected = false
|
||||||
|
|
||||||
|
-- Called when a single value is resolved and resolves if all are done.
|
||||||
|
local function resolveOne(i, ...)
|
||||||
|
if rejected then
|
||||||
|
-- Bail out if this promise has already been rejected.
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
resolvedValues[i] = ...
|
||||||
|
resolvedCount = resolvedCount + 1
|
||||||
|
|
||||||
|
if resolvedCount == #promises then
|
||||||
|
resolve(resolvedValues)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We can assume the values inside `promises` are all promises since we checked above.
|
||||||
|
for i = 1, #promises do
|
||||||
|
promises[i]:andThen(function(...)
|
||||||
|
resolveOne(i, ...)
|
||||||
|
end)
|
||||||
|
:catch(function(...)
|
||||||
|
-- Only reject if this promise is unrejected.
|
||||||
|
if not rejected then
|
||||||
|
reject(...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
|
|
@ -264,4 +264,56 @@ return function()
|
||||||
expect(#chained._values).to.equal(0)
|
expect(#chained._values).to.equal(0)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("Promise.all", function()
|
||||||
|
it("should error if given something other than a table", function()
|
||||||
|
expect(function()
|
||||||
|
Promise.all(1)
|
||||||
|
end).to.throw()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should resolve instantly with an empty table if given no values", function()
|
||||||
|
local promise = Promise.all({})
|
||||||
|
local success, value = promise:await()
|
||||||
|
|
||||||
|
expect(success).to.equal(true)
|
||||||
|
expect(promise:getStatus()).to.equal(Promise.Status.Resolved)
|
||||||
|
expect(value).to.be.a("table")
|
||||||
|
expect(#value).to.equal(0)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should error if given non-promise values", function()
|
||||||
|
expect(function()
|
||||||
|
Promise.all({{}, {}, {}})
|
||||||
|
end).to.throw()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should wait for all promises to be resolved and return their values", function()
|
||||||
|
local promises = {
|
||||||
|
Promise.new(function(resolve)
|
||||||
|
resolve(1)
|
||||||
|
end),
|
||||||
|
Promise.new(function(resolve)
|
||||||
|
resolve("A string")
|
||||||
|
end),
|
||||||
|
Promise.new(function(resolve)
|
||||||
|
resolve(nil)
|
||||||
|
end),
|
||||||
|
Promise.new(function(resolve)
|
||||||
|
resolve(false)
|
||||||
|
end)
|
||||||
|
}
|
||||||
|
|
||||||
|
local promise = Promise.all(promises)
|
||||||
|
local success, resolved = promise:await()
|
||||||
|
|
||||||
|
expect(success).to.equal(true)
|
||||||
|
expect(resolved).to.be.a("table")
|
||||||
|
expect(#resolved).to.equal(4)
|
||||||
|
expect(resolved[1]).to.equal(1)
|
||||||
|
expect(resolved[2]).to.equal("A string")
|
||||||
|
expect(resolved[3]).to.equal(nil)
|
||||||
|
expect(resolved[4]).to.equal(false)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue