mirror of
https://github.com/CFC-Servers/cfc_network_promises.git
synced 2025-03-04 03:03:19 -05:00
Added reject(), bug fixes and rearrange
This commit is contained in:
parent
f35a9ae643
commit
0b226e0425
@ -3,7 +3,7 @@ Warning: This is a complex file!
|
||||
Make sure you are familiar with JavaScript style Promises before attempting to modify this.
|
||||
]]
|
||||
|
||||
local function isPromise( p )
|
||||
function isPromise( p )
|
||||
return type( p ) == "table" and p.next ~= nil
|
||||
end
|
||||
|
||||
@ -23,6 +23,12 @@ local function delayReject( prom, ... )
|
||||
delayPromise( prom, "reject", ... )
|
||||
end
|
||||
|
||||
-- Table for checking yield was from await
|
||||
local awaitFlag = { awaitFlag = true }
|
||||
local function isAwaitFlag( v )
|
||||
return tobool( type( v ) == "table" and v.awaitFlag )
|
||||
end
|
||||
|
||||
--[[
|
||||
How this works:
|
||||
|
||||
@ -64,13 +70,21 @@ promiseReturn = function( f, state, coInput, rootPromiseInput, debugInfoInput )
|
||||
local success = table.remove( ret, 1 )
|
||||
|
||||
if success then
|
||||
-- ret[1] will be a promise whenever await is called
|
||||
if isPromise( ret[1] ) then
|
||||
if isAwaitFlag( ret[1] ) then
|
||||
-- If it's a promise, set that promise's reject and resolve to put it on the promise stack
|
||||
local awaitPromise = ret[2]
|
||||
|
||||
local resolve = promiseReturn( f, true, co, rootPromise, debugInfo )
|
||||
local reject = promiseReturn( f, false, co, rootPromise, debugInfo )
|
||||
|
||||
ret[1]:next( resolve, reject )
|
||||
awaitPromise:next( resolve, reject )
|
||||
elseif isPromise( ret[1] ) then
|
||||
-- Return result was a promise, make it's reject and resolve forward to rootPromise
|
||||
ret[1]:next( function( ... )
|
||||
rootPromise:resolve( ... )
|
||||
end, function( ... )
|
||||
rootPromise:reject( ... )
|
||||
end )
|
||||
else
|
||||
-- If it's not a promise, this means the coroutine finished or the promise stack is empty ( from errors )
|
||||
delayResolve( rootPromise, unpack( ret ) )
|
||||
@ -78,8 +92,12 @@ promiseReturn = function( f, state, coInput, rootPromiseInput, debugInfoInput )
|
||||
else
|
||||
-- There was a normal error in the coroutine, just reject with the error and location
|
||||
local err = ret[1]
|
||||
local locationText = "\nContaining async function defined in " .. debugInfo.short_src .. " at line " .. debugInfo.linedefined .. "\n"
|
||||
delayReject( rootPromise, err, locationText )
|
||||
if type( err ) == "table" and err.reject then
|
||||
delayReject( rootPromise, unpack( err.reject ) )
|
||||
else
|
||||
local locationText = "\nContaining async function defined in " .. debugInfo.short_src .. " at line " .. debugInfo.linedefined .. "\n"
|
||||
delayReject( rootPromise, err, debug.traceback( co ), locationText )
|
||||
end
|
||||
end
|
||||
|
||||
-- We only return the rootPromise at root, else it will cause every "next" call above to be waiting on the rootPromise
|
||||
@ -110,20 +128,25 @@ AwaitTypes = {
|
||||
function await( p, awaitType, arg )
|
||||
assert( coroutine.running(), "Cannot use await outside of async function" )
|
||||
|
||||
if not isPromise( p ) then
|
||||
-- If not a promise, it doesn't need to be waited for
|
||||
return p
|
||||
end
|
||||
|
||||
awaitType = awaitType or AwaitTypes.RETURN
|
||||
local data = { coroutine.yield( p ) }
|
||||
local data = { coroutine.yield( awaitFlag, p ) }
|
||||
local success = table.remove( data, 1 )
|
||||
|
||||
if awaitType == AwaitTypes.RETURN then
|
||||
return success, unpack( data )
|
||||
elseif awaitType == AwaitTypes.PROPAGATE then
|
||||
if not success then
|
||||
error( data[1] )
|
||||
reject( unpack( data ) )
|
||||
end
|
||||
|
||||
return unpack( data )
|
||||
elseif awaitType == AwaitTypes.MESSAGE_OVERRIDE then
|
||||
error( arg )
|
||||
reject( arg )
|
||||
elseif awaitType == AwaitTypes.HANDLER then
|
||||
if not success then
|
||||
arg( unpack( data ) )
|
||||
@ -133,6 +156,30 @@ function await( p, awaitType, arg )
|
||||
end
|
||||
end
|
||||
|
||||
local function stringifyArgs( ... )
|
||||
local out = {}
|
||||
for k, v in pairs{ ... } do
|
||||
if istable( v ) then
|
||||
out[k] = table.ToString( v )
|
||||
else
|
||||
out[k] = tostring( v )
|
||||
end
|
||||
end
|
||||
return table.concat( out, ", " )
|
||||
end
|
||||
|
||||
function reject( ... )
|
||||
if inAsync() then
|
||||
error( { reject = { ... } } )
|
||||
else
|
||||
error( stringifyArgs( ... ) )
|
||||
end
|
||||
end
|
||||
|
||||
function inAsync()
|
||||
return tobool( coroutine.running() )
|
||||
end
|
||||
|
||||
--[[
|
||||
Old simpler await definition, in case we decide to change back.
|
||||
|
||||
|
@ -7,4 +7,4 @@ NP = networkPromise -- shorthand name, networkPromise will get a bit cumbersome
|
||||
include( "network_promises/http.lua" )
|
||||
include( "network_promises/net.lua" )
|
||||
include( "network_promises/async.lua" )
|
||||
include( "network_promises/xdcall.lua" )
|
||||
include( "network_promises/util.lua" )
|
||||
|
@ -86,6 +86,7 @@ function NP.http.requestIndef( method, url, overrides )
|
||||
|
||||
table.Merge( struct, overrides )
|
||||
struct.headers.Authorization = overrides.authToken
|
||||
|
||||
HTTP( struct )
|
||||
|
||||
return prom
|
||||
|
@ -25,20 +25,19 @@ local function finish( deferred, state )
|
||||
end
|
||||
end
|
||||
if state == REJECTED and #deferred.queue == 0 then
|
||||
timer.Simple( 0, function()
|
||||
local errText = ""
|
||||
for k, v in ipairs( deferred.value ) do
|
||||
if type( v ) == "table" then
|
||||
errText = errText .. table.ToString( v ) .. "\n"
|
||||
else
|
||||
errText = errText .. tostring( v ) .. "\n"
|
||||
end
|
||||
local errText = ""
|
||||
for k, v in ipairs( deferred.value ) do
|
||||
if type( v ) == "table" then
|
||||
errText = errText .. table.ToString( v ) .. "\n"
|
||||
else
|
||||
errText = errText .. tostring( v ) .. "\n"
|
||||
end
|
||||
if #errText > 500 then
|
||||
errText = errText:sub( 1, 500 ) .. "..."
|
||||
end
|
||||
error( "Uncaught rejection or exception in promise:\n" .. errText .. "IGNORE FOLLOWING 4 LINES" )
|
||||
end )
|
||||
end
|
||||
if #errText > 1000 then
|
||||
errText = errText:sub( 1, 1000 ) .. "...\n"
|
||||
end
|
||||
ErrorNoHalt( "Uncaught rejection or exception in promise:\n" .. errText )
|
||||
print("hi")
|
||||
end
|
||||
deferred.state = state
|
||||
end
|
||||
@ -58,7 +57,7 @@ local function promise( deferred, next, success, failure, nonpromisecb )
|
||||
failure()
|
||||
end )
|
||||
if not ok and not called then
|
||||
deferred.value = { err, stack }
|
||||
deferred.value = handleError( deferred, { err, stack } )
|
||||
failure()
|
||||
end
|
||||
else
|
||||
@ -66,6 +65,19 @@ local function promise( deferred, next, success, failure, nonpromisecb )
|
||||
end
|
||||
end
|
||||
|
||||
local function handleError( deferred, values )
|
||||
print("handle this shit")
|
||||
local first = values[1]
|
||||
if type( first ) == "table" and first.reject then
|
||||
return unpack( first.reject )
|
||||
end
|
||||
|
||||
table.insert( values, "\nContaining next defined in " .. deferred.successInfo.short_src ..
|
||||
" at line " .. deferred.successInfo.linedefined .. "\n" )
|
||||
|
||||
return values
|
||||
end
|
||||
|
||||
local function fire( deferred )
|
||||
local next
|
||||
if type( deferred.value[1] ) == "table" then
|
||||
@ -79,32 +91,30 @@ local function fire( deferred )
|
||||
fire( deferred )
|
||||
end, function()
|
||||
local ok
|
||||
local v
|
||||
local value
|
||||
if deferred.state == RESOLVING and isfunction( deferred.success ) then
|
||||
local ret = { xdcall( deferred.success, unpack( deferred.value ) ) }
|
||||
ok = table.remove( ret, 1 )
|
||||
v = ret
|
||||
value = ret
|
||||
if not ok then
|
||||
table.insert( ret, "\nContaining next defined in " .. deferred.successInfo.short_src ..
|
||||
" at line " .. deferred.successInfo.linedefined .. "\n" )
|
||||
value = handleError( deferred, value )
|
||||
end
|
||||
elseif deferred.state == REJECTING and isfunction( deferred.failure ) then
|
||||
local ret = { xdcall( deferred.failure, unpack( deferred.value ) ) }
|
||||
ok = table.remove( ret, 1 )
|
||||
v = ret
|
||||
value = ret
|
||||
if ok then
|
||||
deferred.state = RESOLVING
|
||||
else
|
||||
table.insert( ret, "\nContaining next defined in " .. deferred.failureInfo.short_src ..
|
||||
" at line " .. deferred.failureInfo.linedefined .. "\n" )
|
||||
value = handleError( deferred, value )
|
||||
end
|
||||
end
|
||||
|
||||
if ok ~= nil then
|
||||
if ok then
|
||||
deferred.value = v
|
||||
deferred.value = value
|
||||
else
|
||||
deferred.value = v
|
||||
deferred.value = value
|
||||
return finish( deferred )
|
||||
end
|
||||
end
|
||||
|
32
lua/network_promises/util.lua
Normal file
32
lua/network_promises/util.lua
Normal file
@ -0,0 +1,32 @@
|
||||
-- Same as pcall, but returns error message and traceback on error
|
||||
function xdcall( func, ... )
|
||||
local traceback
|
||||
local data = { xpcall( func, function( err )
|
||||
traceback = debug.traceback( tostring( err ) )
|
||||
return err
|
||||
end, ... ) }
|
||||
|
||||
local success = table.remove( data, 1 )
|
||||
|
||||
if success then
|
||||
return true, unpack( data )
|
||||
end
|
||||
|
||||
if type( data[1] ) == "table" and data[1].reject then
|
||||
return false, data[1], traceback
|
||||
end
|
||||
|
||||
return false, traceback
|
||||
end
|
||||
|
||||
-- Hatling error with the little popup, but no traceback
|
||||
function errorNoTrace( err )
|
||||
local mt = {
|
||||
__tostring = function( self )
|
||||
return err
|
||||
end
|
||||
}
|
||||
local errObj = setmetatable( {}, mt )
|
||||
|
||||
error( errObj )
|
||||
end
|
@ -1,16 +0,0 @@
|
||||
-- Same as pcall, but returns error message and traceback on error
|
||||
function xdcall( func, ... )
|
||||
local traceback
|
||||
local data = { xpcall( func, function( err )
|
||||
traceback = debug.traceback()
|
||||
return err
|
||||
end, ... ) }
|
||||
|
||||
local success = table.remove( data, 1 )
|
||||
|
||||
if success then
|
||||
return true, unpack( data )
|
||||
end
|
||||
|
||||
return false, data[1], traceback
|
||||
end
|
Loading…
Reference in New Issue
Block a user