mirror of
https://github.com/AmberGraceRblx/luau-promise.git
synced 2025-04-25 08:00:03 +00:00
Make all/race cancel correctly
This commit is contained in:
parent
3dbb121906
commit
17fac4d008
5 changed files with 48 additions and 18 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
# 2.4.1
|
||||||
|
|
||||||
|
- Fix issue with Promise.race/all always cancelling instead of only cancelling if the Promise has no other consumers
|
||||||
|
|
||||||
# 2.4.0
|
# 2.4.0
|
||||||
|
|
||||||
- `Promise.is` now only checks if the object is "andThennable" (has an `andThen` method).
|
- `Promise.is` now only checks if the object is "andThennable" (has an `andThen` method).
|
||||||
|
|
|
@ -174,6 +174,8 @@ docs:
|
||||||
* is resolved after all input promises resolve.
|
* is resolved after all input promises resolve.
|
||||||
* is rejected if ANY input promises reject.
|
* is rejected if ANY input promises reject.
|
||||||
Note: Only the first return value from each promise will be present in the resulting array.
|
Note: Only the first return value from each promise will be present in the resulting array.
|
||||||
|
|
||||||
|
After any input Promise rejects, all other input Promises that are still pending will be cancelled if they have no other consumers.
|
||||||
static: true
|
static: true
|
||||||
params: "promises: array<Promise<T>>"
|
params: "promises: array<Promise<T>>"
|
||||||
returns: Promise<array<T>>
|
returns: Promise<array<T>>
|
||||||
|
@ -181,7 +183,7 @@ docs:
|
||||||
desc: |
|
desc: |
|
||||||
Accepts an array of Promises and returns a new promise that is resolved or rejected as soon as any Promise in the array resolves or rejects.
|
Accepts an array of Promises and returns a new promise that is resolved or rejected as soon as any Promise in the array resolves or rejects.
|
||||||
|
|
||||||
All other Promises that don't win the race will be cancelled.
|
All other Promises that don't win the race will be cancelled if they have no other consumers.
|
||||||
static: true
|
static: true
|
||||||
params: "promises: array<Promise<T>>"
|
params: "promises: array<Promise<T>>"
|
||||||
returns: Promise<T>
|
returns: Promise<T>
|
||||||
|
|
25
lib/init.lua
25
lib/init.lua
|
@ -227,10 +227,12 @@ function Promise.all(promises)
|
||||||
return Promise.new(function(resolve, reject)
|
return Promise.new(function(resolve, reject)
|
||||||
-- An array to contain our resolved values from the given promises.
|
-- An array to contain our resolved values from the given promises.
|
||||||
local resolvedValues = {}
|
local resolvedValues = {}
|
||||||
|
local newPromises = {}
|
||||||
|
|
||||||
-- Keep a count of resolved promises because just checking the resolved
|
-- Keep a count of resolved promises because just checking the resolved
|
||||||
-- values length wouldn't account for promises that resolve with nil.
|
-- values length wouldn't account for promises that resolve with nil.
|
||||||
local resolvedCount = 0
|
local resolvedCount = 0
|
||||||
|
local finalized = false
|
||||||
|
|
||||||
-- Called when a single value is resolved and resolves if all are done.
|
-- Called when a single value is resolved and resolves if all are done.
|
||||||
local function resolveOne(i, ...)
|
local function resolveOne(i, ...)
|
||||||
|
@ -245,14 +247,26 @@ function Promise.all(promises)
|
||||||
-- We can assume the values inside `promises` are all promises since we
|
-- We can assume the values inside `promises` are all promises since we
|
||||||
-- checked above.
|
-- checked above.
|
||||||
for i = 1, #promises do
|
for i = 1, #promises do
|
||||||
|
if finalized then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(
|
||||||
|
newPromises,
|
||||||
promises[i]:andThen(
|
promises[i]:andThen(
|
||||||
function(...)
|
function(...)
|
||||||
resolveOne(i, ...)
|
resolveOne(i, ...)
|
||||||
end,
|
end,
|
||||||
function(...)
|
function(...)
|
||||||
|
for _, promise in ipairs(newPromises) do
|
||||||
|
promise:cancel()
|
||||||
|
end
|
||||||
|
finalized = true
|
||||||
|
|
||||||
reject(...)
|
reject(...)
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
@ -269,9 +283,11 @@ function Promise.race(promises)
|
||||||
end
|
end
|
||||||
|
|
||||||
return Promise.new(function(resolve, reject, onCancel)
|
return Promise.new(function(resolve, reject, onCancel)
|
||||||
|
local newPromises = {}
|
||||||
|
|
||||||
local function finalize(callback)
|
local function finalize(callback)
|
||||||
return function (...)
|
return function (...)
|
||||||
for _, promise in ipairs(promises) do
|
for _, promise in ipairs(newPromises) do
|
||||||
promise:cancel()
|
promise:cancel()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -279,10 +295,15 @@ function Promise.race(promises)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
onCancel(finalize(reject))
|
if onCancel(finalize(reject)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
for _, promise in ipairs(promises) do
|
for _, promise in ipairs(promises) do
|
||||||
|
table.insert(
|
||||||
|
newPromises,
|
||||||
promise:andThen(finalize(resolve), finalize(reject))
|
promise:andThen(finalize(resolve), finalize(reject))
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -480,8 +480,8 @@ return function()
|
||||||
|
|
||||||
expect(combinedPromise:getStatus()).to.equal(Promise.Status.Started)
|
expect(combinedPromise:getStatus()).to.equal(Promise.Status.Started)
|
||||||
|
|
||||||
resolveB("foo", "bar")
|
|
||||||
rejectA("baz", "qux")
|
rejectA("baz", "qux")
|
||||||
|
resolveB("foo", "bar")
|
||||||
|
|
||||||
local resultLength, result = pack(combinedPromise:_unwrap())
|
local resultLength, result = pack(combinedPromise:_unwrap())
|
||||||
local success, first, second = unpack(result, 1, resultLength)
|
local success, first, second = unpack(result, 1, resultLength)
|
||||||
|
@ -490,6 +490,7 @@ return function()
|
||||||
expect(success).to.equal(false)
|
expect(success).to.equal(false)
|
||||||
expect(first).to.equal("baz")
|
expect(first).to.equal("baz")
|
||||||
expect(second).to.equal("qux")
|
expect(second).to.equal("qux")
|
||||||
|
expect(b:getStatus()).to.equal(Promise.Status.Cancelled)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("should not resolve if resolved after rejecting", function()
|
it("should not resolve if resolved after rejecting", function()
|
||||||
|
@ -574,10 +575,11 @@ return function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("should cancel other promises", function()
|
it("should cancel other promises", function()
|
||||||
|
local promise = Promise.new(function() end)
|
||||||
|
promise:andThen(function() end)
|
||||||
local promises = {
|
local promises = {
|
||||||
Promise.new(function(resolve)
|
promise,
|
||||||
-- resolve(1)
|
Promise.new(function() end),
|
||||||
end),
|
|
||||||
Promise.new(function(resolve)
|
Promise.new(function(resolve)
|
||||||
resolve(2)
|
resolve(2)
|
||||||
end)
|
end)
|
||||||
|
@ -587,8 +589,9 @@ return function()
|
||||||
|
|
||||||
expect(promise:getStatus()).to.equal(Promise.Status.Resolved)
|
expect(promise:getStatus()).to.equal(Promise.Status.Resolved)
|
||||||
expect(promise._values[1]).to.equal(2)
|
expect(promise._values[1]).to.equal(2)
|
||||||
expect(promises[1]:getStatus()).to.equal(Promise.Status.Cancelled)
|
expect(promises[1]:getStatus()).to.equal(Promise.Status.Started)
|
||||||
expect(promises[2]:getStatus()).to.equal(Promise.Status.Resolved)
|
expect(promises[2]:getStatus()).to.equal(Promise.Status.Cancelled)
|
||||||
|
expect(promises[3]:getStatus()).to.equal(Promise.Status.Resolved)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("should error if a non-array table is passed in", function()
|
it("should error if a non-array table is passed in", function()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "roblox-lua-promise"
|
name = "roblox-lua-promise"
|
||||||
version = "2.4.0"
|
version = "2.4.1"
|
||||||
author = "evaera"
|
author = "evaera"
|
||||||
content_root = "lib"
|
content_root = "lib"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue