Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/allSettled(), any(), race() #25

Merged
merged 9 commits into from
Nov 14, 2024
271 changes: 259 additions & 12 deletions src/source/promises.bs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,253 @@ namespace promises
promises.resolve(promiseArray, deferred)
else
' Reject if the supplied list is not an array
promises.reject("Promises.all: did not supply an array")
try
throw "did not supply an array"
catch e
promises.reject(e, deferred)
end try
end if
end if

return deferred
end function

' Allows multiple promise operations to be resolved as a single promise.
function allSettled(promiseArray as dynamic) as dynamic
' Create a deferred to be resolved later
deferred = promises.create()

if type(promiseArray) = "roArray" and not promiseArray.isEmpty() then
' Track the state and results of all the promises
state = {
deferred: deferred
results: []
resolvedCount: 0
total: promiseArray.count()
done: false
}

for i = 0 to promiseArray.count() - 1
promise = promiseArray[i]
if promises.isPromise(promise) then

' Watch for both resolved or rejected promises
promises.onThen(promise, sub(result as dynamic, context as dynamic)

' Do not process any promises that come in late
' This can happen if any of the other promises reject
if not context.state.done then
' Always assign the result to the origin index so results are in the same
' order as the supplied promiseArray
context.state.results[context.index] = { status: promises.PromiseState.resolved, value: result }
context.state.resolvedCount++

if context.state.resolvedCount = context.state.total then
' All the promises are resolved.
' Resolve the deferred and make the state as complete
context.state.done = true
promises.resolve(context.state.results, context.state.deferred)
end if
end if
end sub, { state: state, index: i })

promises.onCatch(promise, sub(error as dynamic, context as dynamic)
' Do not process any promises that come in late
' This can happen if any of the other promises reject
if not context.state.done then
' Always assign the result to the origin index so results are in the same
' order as the supplied promiseArray
context.state.results[context.index] = { status: promises.PromiseState.rejected, reason: error }
context.state.resolvedCount++

if context.state.resolvedCount = context.state.total then
' All the promises are resolved.
' Resolve the deferred and make the state as complete
context.state.done = true
promises.resolve(context.state.results, context.state.deferred)
end if
end if
end sub, { state: state, index: i })
else
' The value in the promise array is not a promise.
' Immediately set the result.
state.results[i] = { status: promises.PromiseState.resolved, value: promise }
state.resolvedCount++

if state.resolvedCount = state.total then
' All the promises are resolved.
' Resolve the deferred and make the state as complete
state.done = true
promises.resolve(state.results, state.deferred)
end if
end if
end for
else
if type(promiseArray) = "roArray" then
' Resolve when the array is empty
promises.resolve(promiseArray, deferred)
else
' Reject if the supplied list is not an array
try
throw "did not supply an array"
catch e
promises.reject(e, deferred)
end try
end if
end if

return deferred
end function

' Allows multiple promise operations to be resolved as a single promise.
function any(promiseArray as dynamic) as dynamic
' Create a deferred to be resolved later
deferred = promises.create()

if type(promiseArray) = "roArray" and not promiseArray.isEmpty() then
' Track the state and results of all the promises
state = {
deferred: deferred
errors: []
resolvedCount: 0
total: promiseArray.count()
done: false
}

for i = 0 to promiseArray.count() - 1
promise = promiseArray[i]
if promises.isPromise(promise) then

if promise.promiseState = promises.PromiseState.resolved then
' Do not process any promises that come in after the first resolved one
if not state.done then
state.done = true
promises.resolve(promise.promiseResult, state.deferred)
end if
else
' Watch for both resolved or rejected promises
promises.onThen(promise, sub(result as dynamic, state as dynamic)
' Do not process any promises that come in after the first resolved one
if not state.done then
state.done = true
promises.resolve(result, state.deferred)
end if
end sub, state)

promises.onCatch(promise, sub(error as dynamic, context as dynamic)
' Do not process any promises that come in late
' This can happen if any of the other promises reject
if not context.state.done then
' Always assign the result to the origin index so results are in the same
' order as the supplied promiseArray
context.state.errors[context.index] = error
context.state.resolvedCount++

if context.state.resolvedCount = context.state.total then
' All the promises are resolved.
' Resolve the deferred and make the state as complete
context.state.done = true
try
throw { message: "All promises were rejected", errors: context.state.errors }
catch e
promises.reject(e, context.state.deferred)
end try
end if
end if
end sub, { state: state, index: i })
end if
else
' The value in the promise array is not a promise.
' Immediately set the result.
if not state.done then
state.done = true
promises.resolve(promise, state.deferred)
end if
end if
end for
else
if type(promiseArray) = "roArray" then
' Resolve when the array is empty
promises.resolve(promiseArray, deferred)
else
' Reject if the supplied list is not an array
try
throw "did not supply an array"
catch e
promises.reject(e, deferred)
end try
end if
end if

return deferred
end function

' Allows multiple promise operations to be resolved as a single promise.
function race(promiseArray as dynamic) as dynamic
' Create a deferred to be resolved later
deferred = promises.create()

if type(promiseArray) = "roArray" and not promiseArray.isEmpty() then
' Track the state and results of all the promises
state = {
deferred: deferred
done: false
}

for i = 0 to promiseArray.count() - 1
promise = promiseArray[i]
if promises.isPromise(promise) then

if promise.promiseState = promises.PromiseState.resolved then
' Do not process any promises that come in after the first resolved one
if not state.done then
state.done = true
promises.resolve(promise.promiseResult, state.deferred)
end if
else if promise.promiseState = promises.PromiseState.rejected then
' Do not process any promises that come in after the first resolved one
if not state.done then
state.done = true
promises.reject(promise.promiseResult, state.deferred)
end if
else
' Watch for both resolved or rejected promises
promises.onThen(promise, sub(result as dynamic, state as dynamic)
' Do not process any promises that come in after the first resolved one
if not state.done then
state.done = true
promises.resolve(result, state.deferred)
end if
end sub, state)

promises.onCatch(promise, sub(error as dynamic, state as dynamic)
' Do not process any promises that come in after the first resolved one
if not state.done then
state.done = true
promises.reject(error, state.deferred)
end if
end sub, state)
end if
else
' The value in the promise array is not a promise.
' Immediately set the result.
if not state.done then
state.done = true
promises.resolve(promise, state.deferred)
end if
end if
end for
else
if type(promiseArray) = "roArray" then
' Resolve when the array is empty
promises.resolve(promiseArray, deferred)
else
' Reject if the supplied list is not an array
try
throw "did not supply an array"
catch e
promises.reject(e, deferred)
end try
end if
end if

Expand All @@ -128,7 +374,7 @@ namespace promises
else
promise.update({ promiseResult: result }, true)
end if
promise.promiseState = promises.internal.PromiseState.resolved
promise.promiseState = promises.PromiseState.resolved
end if
return promise
end function
Expand All @@ -145,13 +391,13 @@ namespace promises
else
promise.update({ promiseResult: error }, true)
end if
promise.promiseState = promises.internal.PromiseState.rejected
promise.promiseState = promises.PromiseState.rejected
end if
return promise
end function

function isComplete(promise as object) as boolean
return promises.isPromise(promise) and (promise.promiseState = promises.internal.PromiseState.resolved or promise.promiseState = promises.internal.PromiseState.rejected)
return promises.isPromise(promise) and (promise.promiseState = promises.PromiseState.resolved or promise.promiseState = promises.PromiseState.rejected)
end function

' Determines if the given item is a promise.
Expand Down Expand Up @@ -224,14 +470,15 @@ namespace promises
if promises.isPromise(value) then return value
return promises.resolve(value)
end function
end namespace

namespace promises.internal
enum PromiseState
pending = "pending"
resolved = "resolved"
rejected = "rejected"
end enum
end namespace

namespace promises.internal

enum PromiseField
promiseState = "promiseState"
Expand Down Expand Up @@ -285,7 +532,7 @@ namespace promises.internal

promiseState = promise.promiseState
'trigger a change if the promise is already resolved
if promiseState = promises.internal.PromiseState.resolved or promiseState = promises.internal.PromiseState.rejected then
if promiseState = promises.PromiseState.resolved or promiseState = promises.PromiseState.rejected then
promises.internal.delay(sub (details as object)
details.promise.promiseState = details.promiseState
end sub, { promise: promise, promiseState: promiseState })
Expand Down Expand Up @@ -328,12 +575,12 @@ namespace promises.internal

'handle .then() listeners
for each listener in promiseStorage.thenListeners
promises.internal.processPromiseListener(originalPromise, listener, promiseState = promises.internal.PromiseState.resolved, promiseResult)
promises.internal.processPromiseListener(originalPromise, listener, promiseState = promises.PromiseState.resolved, promiseResult)
end for

'handle .catch() listeners
for each listener in promiseStorage.catchListeners
promises.internal.processPromiseListener(originalPromise, listener, promiseState = promises.internal.PromiseState.rejected, promiseResult)
promises.internal.processPromiseListener(originalPromise, listener, promiseState = promises.PromiseState.rejected, promiseResult)
end for

'handle .finally() listeners
Expand Down Expand Up @@ -416,7 +663,7 @@ namespace promises.internal
end try
else
'use the current promise value to pass to the next promise (this is a .catch handler)
if originalPromise.promiseState = promises.internal.PromiseState.rejected then
if originalPromise.promiseState = promises.PromiseState.rejected then
callbackResult = promises.reject(promiseValue)
else
callbackResult = promiseValue
Expand All @@ -431,13 +678,13 @@ namespace promises.internal
promiseState = context.callbackPromise.promiseState
promiseResult = context.callbackPromise.promiseResult

if promiseState = promises.internal.PromiseState.resolved then
if promiseState = promises.PromiseState.resolved then
'the callback promise is complete. resolve the newPromise
promises.resolve(promiseResult, context.newPromise)
return
end if

if promiseState = promises.internal.PromiseState.rejected then
if promiseState = promises.PromiseState.rejected then
promises.reject(promiseResult, context.newPromise)
return
end if
Expand Down
Loading
Loading