Android Question How to return two parameters using a resumable sub?

dennmtr

Member
Licensed User
[CODE lang="b4x" highlight="4,10"]
B4X:
Public Sub MySub ' In Another Component
    Sleep(1000)
    CallSub3(Sender, "MySub_Complete", True, Null)
End Sub

Public Sub MySub2 ' In Current Component
    Dim SenderObject As Object = MySub
    Wait For (SenderObject) MySub_Complete (Success As Boolean, Result As Object)
    Log(Success)
End Sub

Public Sub MySub2
'Avoid MySub
'Avoid Wait For MySub_Complete(Success As Boolean, Result As Object)
Wait For (MySub) Complete (Success As Boolean, Result As Object)
End Sub[/CODE]

I want to avoid using callsub, but i dont know how to return more than one parameters as the example above!

Or something like this:

B4X:
    Dim SenderFilter As Object = SQL_.ExecQueryAsync("SQL", QueryString, Args)
    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, Rs As ResultSet)

How SQL_.ExecQueryAsync Returns the two values without using component value that callsub needs?

This doesnt work even not fails:

B4X:
Public Sub MySubInAnotherComponent
    Sleep(1000)
    CallSub3(Sender, "MySubInAnotherComponent_Complete", True, Null)
End Sub

Public Sub MyCurrentSub
    Dim SenderObject As Object = MySubInAnotherComponent
    Wait For (SenderObject) MySubInAnotherComponent_Complete (Success As Boolean, Result As Object)
    Log(Success)
End Sub
 
Last edited:
As a ResumableSub (in contrast to an event that you WaitFor) can only return a single values then return a List or an Array containing the required values.
Yes but i dont like this approach :(, too messy
 
Upvote 0
Someone asked the same question in this link.
 
Upvote 0
Someone asked the same question in this link.

Nope, i will use the Component, EventName approach
Still SQL's lib approach is a mystery
 
Upvote 0
Still SQL's lib approach is a mystery
No it's not. As I said in post #2 you can can wait for events as well as Resumable Subs. Events can have more than one parameter and when you wait for an event your parameter list in the Wait For has to match that of the event. If you wait for an event then the actual event Sub, if present, will not be executed as the Wait For pre-empts the event and swallows it. SQL QueryComplete is an event. However as Subs can only return a single value when you Wait For a Resumable Sub you can only have a single parameter in the Wait For parameter list.
 
Upvote 0
No it's not. As I said in post #2 you can can wait for events as well as Resumable Subs. Events can have more than one parameter and when you wait for an event your parameter list in the Wait For has to match that of the event. If you wait for an event then the actual event Sub, if present, will not be executed as the Wait For pre-empts the event and swallows it. SQL QueryComplete is an event. However as Subs can only return a single value when you Wait For a Resumable Sub you can only have a single parameter in the Wait For parameter list.
Corrent but this still doesnt solves the mystery on how SQL sub call back the event to the component without having the component passed as object. maybe is some java inside something like callsub(this, eventname + "")
 
Last edited:
Upvote 0
how SQL sub call back the event to the component without having the component passed as object.
Component is not the really the correct term, object instance is more correct. The event Sub name prefix, in this case 'SQL', indicates to the object instance which Sub it is to call for the event. Usually the event name is passed to an object instance when it is initialised but in the case of SQL it is passed to each ExecQueryAsync or ExecNonQueryBatch call as in your code fragment above
 
Upvote 0
I do it the way agraham says -and I would listen to what he says, not just as an expert, but as someone who has even been involved in some way in the initial development of B4X (correct me if I'm wrong) - and I don't think it's too messy. Just add the parameters to a map, and return it.
Or the way LucasMs says, just return a custom type, "Rs As ResultSet" is a custom type indeed

Code:
Public Sub GetRecord (Command As String, parameters() As String) As ResumableSub
    Dim Answer As Map
    Answer.Initialize
    Dim req As DBRequestManager = CreateRequest
    Dim cmd As DBCommand = CreateCommand(Command, parameters)
    Wait For (req.ExecuteQuery(cmd, 0, Null)) JobDone(j As HttpJob)
    Answer.Put("status", j.Success)
    If j.Success Then
        req.HandleJobAsync(j, "req")
        Wait For (req) req_Result(Res As DBResult)
        'work with result
      ' req.PrintTable(Res)
        Answer.Put("Status","Data read succesfuly")
    Else
        Log("ERROR: " & j.ErrorMessage)
        Answer.Put("Status","Error getting data. Error: " & j.ErrorMessage)
    End If
    j.Release
    Answer.Put("Data",Res)

    Return Answer
End Sub
 
Upvote 0
I do it the way agraham says -and I would listen to what he says, not just as an expert, but as someone who has even been involved in some way in the initial development of B4X (correct me if I'm wrong) - and I don't think it's too messy. Just add the parameters to a map, and return it.
Or the way LucasMs says, just return a custom type, "Rs As ResultSet" is a custom type indeed

Code:
Public Sub GetRecord (Command As String, parameters() As String) As ResumableSub
    Dim Answer As Map
    Answer.Initialize
    Dim req As DBRequestManager = CreateRequest
    Dim cmd As DBCommand = CreateCommand(Command, parameters)
    Wait For (req.ExecuteQuery(cmd, 0, Null)) JobDone(j As HttpJob)
    Answer.Put("status", j.Success)
    If j.Success Then
        req.HandleJobAsync(j, "req")
        Wait For (req) req_Result(Res As DBResult)
        'work with result
      ' req.PrintTable(Res)
        Answer.Put("Status","Data read succesfuly")
    Else
        Log("ERROR: " & j.ErrorMessage)
        Answer.Put("Status","Error getting data. Error: " & j.ErrorMessage)
    End If
    j.Release
    Answer.Put("Data",Res)

    Return Answer
End Sub
Me finally:

[CODE lang="b4x" title="ResponseBuilder.bas"]Sub Class_Globals
Private Success_ As Boolean
Private Data_ As Object
Private Params_ As Map
End Sub

Public Sub Initialize

End Sub

Public Sub getSuccess As Boolean
Return Success_
End Sub

Public Sub setSuccess(Success As Boolean)
Success_ = Success
End Sub

Public Sub getFailed As Boolean
Return Not(Success_)
End Sub

Public Sub getData As Object
Return Data_
End Sub

Public Sub setData(Data As Object)
Data_ = Data
End Sub

Public Sub getResultSet As ResultSet
Return Data_
End Sub

Public Sub getAsByte As Byte
Return Data_
End Sub

Public Sub getAsShort As Short
Return Data_
End Sub

Public Sub getAsInt As Int
Return Data_
End Sub

Public Sub getAsLong As Long
Return Data_
End Sub

Public Sub getAsDouble As Double
Return Data_
End Sub

Public Sub getAsFloat As Float
Return Data_
End Sub

Public Sub getAsString As String
Return Data_
End Sub

Public Sub getAsBinary As Byte()
Return Data_
End Sub

Public Sub getAsArray As String()
Return Data_
End Sub

Public Sub getAsArrayInt As Int()
Return Data_
End Sub

Public Sub getAsList As List
Return Data_
End Sub

Public Sub getAsMap As Map
Return Data_
End Sub

Public Sub First As Object
Dim Rs As ResultSet
Rs = Data_
Rs.NextRow
Return Rs.GetString2(0)
End Sub

Public Sub Count As Long
If Success_ Then
Dim Rs As ResultSet
Rs = Data_
Return Rs.RowCount
End If
Return 0
End Sub

Public Sub Any As Boolean
Dim Value As String = FirstOrDefault
If Value = Null Then
Return False
End If
Return Value.As(Long) > 0
End Sub

Public Sub FirstOrDefault As String
If Success_ Then
Dim Rs As ResultSet
Rs = Data_
If Rs.RowCount > 0 Then
Rs.Position = 0
Return Rs.GetString2(0)
End If
End If
Return Null
End Sub

Public Sub List As List
Dim List1 As List
List1.Initialize
If Success_ Then
Dim Rs As ResultSet
Rs = Data_
If Rs.RowCount > 0 Then
Rs.Position = 0
Do Until Rs.Position > Rs.RowCount - 1
List1.Add(Rs.GetString2(0))
If Not(Rs.NextRow) Then
Rs.Position = 0
Exit
End If
Loop
End If
End If
Return List1
End Sub

Public Sub Map As Map
Dim Map1 As Map
Map1.Initialize
If Success_ Then
Dim Rs As ResultSet
Rs = Data_
If Rs.RowCount > 0 Then
Rs.Position = 0
For i = 0 To Rs.ColumnCount - 1
Map1.Put(Rs.GetColumnName(i), Rs.GetString2(i))
Next
End If
End If
Return Map1
End Sub

Public Sub ListMap As List
Dim List1 As List
List1.Initialize
If Success_ Then
Dim Rs As ResultSet
Rs = Data_
If Rs.RowCount > 0 Then
Rs.Position = 0
Do Until Rs.Position > Rs.RowCount - 1
Dim Map1 As Map
Map1.Initialize
For i = 0 To Rs.ColumnCount - 1
Map1.Put(Rs.GetColumnName(i), Rs.GetString2(i))
Next
List1.Add(Map1)
If Not(Rs.NextRow) Then
Rs.Position = 0
Exit
End If
Loop
End If
End If
Return List1
End Sub

Public Sub GetParam(Key As Object) As Object
If Not(Params_.IsInitialized) Then
Return Null
End If
Return Params_.GetDefault(Key, Null)
End Sub

Public Sub SetParam(Key As Object, Value As Object)
If Not(Params_.IsInitialized) Then
Params_.Initialize
End If
Params_.Put(Key, Value)
End Sub[/CODE]

[CODE lang="b4x" title="Response.bas"]'Code module
'Subs in this code module will be accessible from all modules.
Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
End Sub

Public Sub Create(Success As Boolean, Data As Object) As ResponseBuilder
Dim Response As ResponseBuilder
Response.Initialize
Response.Success = Success
Response.Data = Data
Return Response
End Sub[/CODE]


B4X:
Public Sub ExecQueryAsync2(QueryString As String, Args As List) As ResumableSub
    Dim SenderFilter As Object = SQL_.ExecQueryAsync("SQL", QueryString, Args)
    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, Rs As Object) 'Rs As ResultSet will throw an uncatchable exception if query fail
    Return Response.Create(Success, Rs)
End Sub
 
Last edited:
Upvote 0
Why not inline the Create Sub code after Wait For and return the ResponseBuilder instance from the ExecQueryAsync2 ResumableSub?
Look the difference on those two examples:

[CODE lang="b4x" title="UserRepository.bas"]Public Sub GetUsers3(UserId As Long, Columns() As String, PageNumber As Int, PageSize As Int) As Query
Dim Users As Query
Users.Initialize("users")
Users = Users _
.PrimaryKey("id") _
.SelectColumns(Columns) _
.Join("user_relations", Expression.Create2("user_relations", "user_id").Eq3("users", "id")) _
.Where(Expression.Create2("user_relations", "user_profile_id").Eq(UserId)) _
.Skip((PageNumber - 1) * PageSize) _
.Take(PageSize)
Return Users
End Sub

Public Sub GetUsers3(UserId As Long, Columns() As String, PageNumber As Int, PageSize As Int) As Query
Dim Expression1 As ExpressionBuilder
Expression1.Initialize
Expression1.TableName = "user_relations"
Expression1.ColumnName = "user_id"
Expression1 = Expression1.Eq3("users", "id")
Dim Expression2 As ExpressionBuilder
Expression2.Initialize
Expression2.TableName = "user_relations"
Expression2.ColumnName = "user_profile_id"
Expression2 = Expression2.Eq(UserId)
Dim Users As Query
Users.Initialize("users")
Users = Users _
.PrimaryKey("id") _
.SelectColumns(Columns) _
.Join("user_relations", Expression1) _
.Where(Expression2) _
.Skip((PageNumber - 1) * PageSize) _
.Take(PageSize)
Return Users
End Sub[/CODE]
 
Upvote 0
Look the difference on those two examples:
Sorry - but I have no idea what you mean by that. I meant

B4X:
Public Sub ExecQueryAsync2(QueryString As String, Args As List) As ResumableSub
    Dim SenderFilter As Object = SQL_.ExecQueryAsync("SQL", QueryString, Args)
    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, Rs As ResultSet)    
    Dim Response As ResponseBuilder
    Response.Initialize
    Response.Success = Success
    Response.Data = Data.As(Object)
    Return Response
End Sub
I'm not familiar with ResponseBuilder but as you cast it to Object in Sub Create I've cast it to Object in the assignment to Response.Data. That might not be needed depending upon the type expected.
 
Upvote 0
Back
Top