refactor implementation to support cancel

This commit is contained in:
otrepanier 2020-11-30 16:34:13 -08:00
parent df07638f43
commit 1ac7d3cae9
3 changed files with 55 additions and 20 deletions

View file

@ -359,15 +359,27 @@ docs:
- name: fold
since: unreleased
desc: |
Folds an array into a promise or a value. The array is traversed sequentially and
Folds an array of values or promises into a single value. The array is traversed sequentially.
The reducer function can return a promise or directly a value. It always received the last resolved value.
The reducer function can return a promise or value directly. Each iteration receives the resolved value from the previous, and the first receives your defined initial value.
The folding will stop at the first rejection encountered.
```lua
local basket = {"blueberry", "melon", "pear", "melon"}
Promise.fold(basket, function(accumulatedCost, fruit)
if fruit == "blueberry" then
return cost -- blueberries are free!
else
-- call a function that returns a promise with the fruit price
return fetchPrice(fruit):andThen(function(fruitCost)
return cost + fruitCost
end)
end
end, 0)
```
params:
- name: list
type: "array<T>"
type: "array<T | Promise<T>>"
- name: reducer
desc: The function to call with the accumulated value, the current element from the array and its index.
type:
@ -376,7 +388,7 @@ docs:
returns: U | Promise<U>
- name: initialValue
type: "U"
returns: U | Promise<U>
returns: Promise<U>
static: true
- name: each

View file

@ -443,18 +443,16 @@ function Promise.fold(list, callback, initialValue)
assert(type(list) == "table", "Bad argument #1 to Promise.fold: must be a table")
assert(type(callback) == "function", "Bad argument #2 to Promise.fold: must be a function")
local previousValue = initialValue
for i = 1, #list do
local element = list[i]
if Promise.is(previousValue) then
previousValue = previousValue:andThen(function(previousValueResolved)
return callback(previousValueResolved, element, i)
local accumulator = initialValue
return Promise.each(list, function(resolvedElement, i)
if Promise.is(accumulator) then
accumulator = accumulator:andThen(function(previousValueResolved)
return callback(previousValueResolved, resolvedElement, i)
end)
else
previousValue = callback(previousValue, element, i)
accumulator = callback(accumulator, resolvedElement, i)
end
end
return previousValue
end):andThenReturn(accumulator)
end
function Promise.some(promises, amount)

View file

@ -812,30 +812,37 @@ return function()
end)
describe("Promise.fold", function()
it("should return the initial value when the list is empty", function()
it("should return the initial value in a promise when the list is empty", function()
local initialValue = {}
local result = Promise.fold({}, function()
error("should not be called")
end, initialValue)
expect(result).to.equal(initialValue)
expect(Promise.is(result)).to.equal(true)
expect(result:getStatus()).to.equal(Promise.Status.Resolved)
expect(result:expect()).to.equal(initialValue)
end)
it("should fold the list if the reducer never returns promises", function()
local sum = Promise.fold({1, 2, 3}, function(sum, element)
it("should accept promises in the list", function()
local sum = Promise.fold({Promise.resolve(1), 2, 3}, function(sum, element)
return sum + element
end, 0)
expect(sum).to.equal(6)
expect(Promise.is(sum)).to.equal(true)
expect(sum:getStatus()).to.equal(Promise.Status.Resolved)
expect(sum:expect()).to.equal(6)
end)
it("should fold the list into a promise if the reducer returns at least a promise", function()
it("should always return a promise even if the list or reducer don't use them", function()
local sum = Promise.fold({1, 2, 3}, function(sum, element, index)
if index == 2 then
return Promise.resolve(sum + element)
return Promise.delay(1):andThenReturn(sum + element)
else
return sum + element
end
end, 0)
expect(Promise.is(sum)).to.equal(true)
expect(sum:getStatus()).to.equal(Promise.Status.Started)
advanceTime(2)
expect(sum:getStatus()).to.equal(Promise.Status.Resolved)
expect(sum:expect()).to.equal(6)
end)
@ -854,6 +861,24 @@ return function()
expect(status).to.equal(Promise.Status.Rejected)
expect(rejection).to.equal(errorMessage)
end)
it("should return the first canceled promise", function()
local secondPromise
local sum = Promise.fold({1, 2, 3}, function(sum, element, index)
if index == 1 then
return sum + element
elseif index == 2 then
secondPromise = Promise.delay(1):andThenReturn(sum + element)
return secondPromise
else
error('this should not run if the promise is cancelled')
end
end, 0)
expect(Promise.is(sum)).to.equal(true)
expect(sum:getStatus()).to.equal(Promise.Status.Started)
secondPromise:cancel()
expect(sum:getStatus()).to.equal(Promise.Status.Cancelled)
end)
end)
describe("Promise.race", function()