Add calls, awaitValue

This commit is contained in:
Eryn Lynn 2019-09-12 20:13:29 -04:00
parent 3d53e7a364
commit 783eb1c2bb
4 changed files with 122 additions and 32 deletions

View file

@ -1,3 +1,8 @@
# 2.1.0
- Add `finallyCall`, `andThenCall`
- Add `awaitValue`
# 2.0.0
- Add Promise.race

View file

@ -52,7 +52,7 @@ end
## Cancellable animation sequence
The following is an example of an animation sequence which is composable and cancellable. If the sequence is cancelled, the animated part will instantly jump to the end position as if it had played all the way through.
We use `finally` instead of `andThen` because we want the Promises to run even if the Promise is cancelled. We handle the case of the Promise being cancelled with the `onCancel` function.
We use <ApiLink to="Promise.finallyCall" />, which uses `finally` internally, instead of `andThen` because we want the Promises to run even if the Promise is cancelled. We handle the case of the Promise being cancelled with the `onCancel` function.
We take advantage of Promise chaining by returning Promises from the `finally` handler functions. Because of this behavior, cancelling the final Promise in the chain will propagate up to the very top and cancel every single Promise you see here.
@ -69,42 +69,38 @@ local function apply(obj, props)
end
local function runTween(obj, props)
return function()
return Promise.new(function(resolve, reject, onCancel)
local tween = TweenService:Create(obj, TweenInfo.new(0.5), props)
if onCancel(function()
tween:Cancel()
apply(obj, props)
end) then return end
tween.Completed:Connect(resolve)
tween:Play()
end)
end
return Promise.new(function(resolve, reject, onCancel)
local tween = TweenService:Create(obj, TweenInfo.new(0.5), props)
if onCancel(function()
tween:Cancel()
apply(obj, props)
end) then return end
tween.Completed:Connect(resolve)
tween:Play()
end)
end
local function runAnimation(part, intensity)
return function()
return Promise.resolve()
:finally(sleep(1))
:finally(runTween(part, {
Reflectance = 1 * intensity
})):finally(runTween(part, {
CFrame = CFrame.new(part.Position) *
CFrame.Angles(0, math.rad(90 * intensity), 0)
})):finally(runTween(part, {
Size = (
Vector3.new(10, 10, 10) * intensity
) + Vector3.new(1, 1, 1)
}))
end
return Promise.resolve()
:finallyCall(sleep, 1))
:finallyCall(runTween, part, {
Reflectance = 1 * intensity
}):finallyCall(runTween, part, {
CFrame = CFrame.new(part.Position) *
CFrame.Angles(0, math.rad(90 * intensity), 0)
}):finallyCall(runTween, part, {
Size = (
Vector3.new(10, 10, 10) * intensity
) + Vector3.new(1, 1, 1)
})
end
local animation = Promise.resolve() -- Begin Promise chain
:finally(runAnimation(workspace.Part, 1))
:finally(sleep(1))
:finally(runAnimation(workspace.Part, 0))
:finallyCall(runAnimation, workspace.Part, 1)
:finallyCall(sleep, 1)
:finallyCall(runAnimation, workspace.Part, 0)
wait(2)
animation:cancel() -- Remove this line to see the full animation

View file

@ -261,7 +261,47 @@ docs:
returns: Promise<T>
returns: Promise<T>
- name: andThenCall
desc: |
Attaches an `andThen` handler to this Promise that calls the given callback with the predefined arguments. The resolved value is discarded.
```lua
promise:andThenCall(someFunction, "some", "arguments")
```
This is sugar for
```lua
promise:andThen(function()
return callback(...args)
end)
```
params:
- name: callback
type:
kind: function
params: "...: ...any?"
returns: "any"
- name: "..."
type: "...any?"
desc: Arguments which will be passed to the callback.
returns: Promise
- name: finallyCall
desc: |
Same as `andThenCall`, except for `finally`.
Attaches a `finally` handler to this Promise that calls the given callback with the predefined arguments.
params:
- name: callback
type:
kind: function
params: "...: ...any?"
returns: "any"
- name: "..."
type: "...any?"
desc: Arguments which will be passed to the callback.
returns: Promise
- name: cancel
desc: |
@ -272,7 +312,12 @@ docs:
Promises will only be cancelled if all of their consumers are also cancelled. This is to say that if you call `andThen` twice on the same promise, and you cancel only one of the child promises, it will not cancel the parent promise until the other child promise is also cancelled.
- name: await
desc: Yields the current thread until the given Promise completes. Returns true if the Promise resolved, followed by the values that the promise resolved or rejected with.
desc: |
Yields the current thread until the given Promise completes. Returns true if the Promise resolved, followed by the values that the promise resolved or rejected with.
::: warning
If the Promise gets cancelled, this function will return `false`, which is indistinguishable from a rejection. If you need to differentiate, you should use [[Promise.awaitStatus]] instead.
:::
returns:
- desc: "`true` if the Promise successfully resolved."
type: boolean
@ -286,6 +331,14 @@ docs:
desc: The Promise's status.
- type: ...any?
desc: The values that the Promise resolved or rejected with.
- name: awaitValue
desc: |
Yields the current thread until the given Promise completes. Returns the the values that the promise resolved with.
Errors if the Promise rejects or gets cancelled.
returns:
- type: ...any?
desc: The values that the Promise resolved with.
- name: getStatus
desc: Returns the current Promise status.

View file

@ -400,6 +400,16 @@ function Promise.prototype:catch(failureCallback)
return self:andThen(nil, failureCallback)
end
--[[
Calls a callback on `andThen` with specific arguments.
]]
function Promise.prototype:andThenCall(callback, ...)
local length, values = pack(...)
return self:andThen(function()
return callback(unpack(values, 1, length))
end)
end
--[[
Cancels the promise, disallowing it from rejecting or resolving, and calls
the cancellation hook if provided.
@ -466,6 +476,16 @@ function Promise.prototype:finally(finallyHandler)
end, self)
end
--[[
Calls a callback on `finally` with specific arguments.
]]
function Promise.prototype:finallyCall(callback, ...)
local length, values = pack(...)
return self:finally(function()
return callback(unpack(values, 1, length))
end)
end
--[[
Yield until the promise is completed.
@ -504,6 +524,22 @@ function Promise.prototype:await(...)
return status == Promise.Status.Resolved, unpack(result, 1, length - 1)
end
--[[
Calls await and only returns if the Promise resolves.
Throws if the Promise rejects or gets cancelled.
]]
function Promise.prototype:awaitValue(...)
local length, result = pack(self:awaitStatus(...))
local status = table.remove(result, 1)
assert(
status == Promise.Status.Resolved,
tostring(result[1] == nil and "" or result[1])
)
return unpack(result, 1, length - 1)
end
--[[
Intended for use in tests.