New feature: Resumable Subs - simpler asynchronous programming

Erel

B4X founder
Staff member
Licensed User
Longtime User
First example with Wait For:
B4X:
Sub AppStart (Form1 As Form, Args() As String)
   MainForm = Form1
   MainForm.RootPane.LoadLayout("1")
   gmap.Initialize2("gmap", Null, "xxxxxxxxYNLkvBkQ2LhGujrM")
   Pane1.AddNode(gmap.AsPane, 0, 0, Pane1.Width, Pane1.Height)
   MainForm.Show
  
   Wait For Gmap_Ready '<----------------
  
   btnResetMap.Enabled = True
   btnJumpToEiffel.Enabled = True
   For i = 1 To 10
      gmap.AddMarker(10 * i, 10 * i, "Marker #" & i)
   Next
End Sub

This is equivalent to handling the GoogleMap_Ready event in a different sub. The advantage is that you handle everything in the same sub and you can access the local variables.

And a bit more sophisticated usage:
A method that downloads an image and sets it to an ImageView:

test.gif



B4X:
Sub AppStart (Form1 As Form, Args() As String)
   MainForm = Form1
   MainForm.RootPane.LoadLayout("1")
   MainForm.Show
   SetImage(ImageView1, "https://cdn.pixabay.com/photo/2016/09/04/08/46/butterfly-1643510_960_720.jpg")
   SetImage(ImageView2, "https://cdn.pixabay.com/photo/2017/03/29/07/29/windmill-2184353__340.jpg")
   SetImage(ImageView3, "https://cdn.pix3abay.com/photo/2017/04/02/09/08/bulldozer-2195329__340.jpg")
End Sub

Sub SetImage(iv As ImageView, link As String)
   'show a progress indicator while downloading
   Dim pb As ProgressIndicator
   pb.Initialize("")
   Dim parent As Pane = iv.Parent
   parent.AddNode(pb, iv.Left, iv.Top, iv.Width, iv.Height)
   pb.Progress = -1
   Dim j As HttpJob
   j.Initialize("j", Me)
   j.Download(link)
   'wait for JobDone event
   Wait For (j) JobDone(j As HttpJob)
   'Remove the progress indicator
   pb.RemoveNodeFromParent
   If j.Success Then
     iv.SetImage(j.GetBitmap)
   Else
     Log(j.ErrorMessage)
     iv.SetImage(fx.LoadImage(File.DirAssets, "Sorry-image-not-available.png"))
   End If
End Sub

Example of downloading three images one after another (a very common question in the forum):
B4X:
Sub MakeSeveralDownloadsSequentially
   Dim j As HttpJob
   j.Initialize("j", Me)
   j.Download("https://cdn.pixabay.com/photo/2016/09/04/08/46/butterfly-1643510_960_720.jpg")
   Wait For JobDone(j As HttpJob)
   If j.Success Then
     Log("First image downloaded successfully")
   End If
   j.Release

   j.Initialize("j", Me)
   j.Download("https://cdn.pixabay.com/photo/2017/03/29/07/29/windmill-2184353__340.jpg")
   Wait For JobDone(j As HttpJob)
   If j.Success Then
     Log("Second image downloaded successfully")
   End If
   j.Release

   j.Initialize("j", Me)
   j.Download("https://cdn.pixabay.com/photo/2017/04/02/09/08/bulldozer-2195329__340.jpg")
   Wait For JobDone(j As HttpJob)
   If j.Success Then
     Log("Third image downloaded successfully")
   End If
   j.Release
End Sub
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Yes, you can use Sleep instead of CallSubDelayed:
Instead of:
B4X:
Sub Rec (i as int, lim as int)
   if i < lim then
     dosomething
     callsubdelayed3(me,"rec",i+1,50)
   end if
end sub

You can do:
B4X:
Sub Rec
For i = 1 to 1000
  DoSomething
  Sleep(0)
  'Or sleep every 10 iterations
  If i Mod 10 = 0 Then
    Sleep(0)
  End If
Next
End Sub

No need to split the work. You can handle it all inside the sub.
 

aaronk

Well-Known Member
Licensed User
Longtime User
Does this mean I can do something like:

B4X:
Socket.connect(IP,Port,0)
Sleep(4000)
If socket.connected = False Then
    log("failed to connect")
  else
    log("connected OK")
End if

Based on the above example, connect to a TCP/IP socket then wait 4 seconds, and then log if it connected or not.

Is that what this sleep feature does ?

I know I could log once it connects or when it fails etc. but just using the above as an example.

I see that it will be added to B4J first and probably be available in May or June, what about B4A & B4i.. Any ETA when it will be added to them as well providing it is successful in B4J in May/June ?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Does this mean I can do something like:
Yes, but it is better to use Wait For in this case:
B4X:
Socket.Connect(IP, Port, 4000)
Wait For Socket_Connected (Success As Boolean)
If Success Then 
 ...
Else
 ...
End If

what about B4A & B4i.. Any ETA when it will be added to them as well providing it is successful in B4J in May/June ?
It will probably be available in B4A and B4i in June or July.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Quiz:
What is the output of this code:
B4X:
'non-ui project!!!
Sub AppStart (Args() As String)
   DoSomething
   StartMessageLoop
   Log("done!")
End Sub

Sub DoSomething
   For i = 1 To 10
     Log(i)
     Sleep(100)
   Next
   StopMessageLoop
End Sub

And this one:
B4X:
'non-ui project!!!
Sub AppStart (Args() As String)
   For i = 1 To 10
     Log(i)
     Sleep(100)
   Next
   StartMessageLoop
   Log("done!")
End Sub

Tip: Sleep ≈ Return
 

narek adonts

Well-Known Member
Licensed User
Longtime User
Quiz:
What is the output of this code:
B4X:
'non-ui project!!!
Sub AppStart (Args() As String)
   DoSomething
   StartMessageLoop
   Log("done!")
End Sub

Sub DoSomething
   For i = 1 To 10
     Log(i)
     Sleep(100)
   Next
   StopMessageLoop
End Sub

And this one:
B4X:
'non-ui project!!!
Sub AppStart (Args() As String)
   For i = 1 To 10
     Log(i)
     Sleep(100)
   Next
   StartMessageLoop
   Log("done!")
End Sub

Tip: Sleep ≈ Return

1.

1
2
3
4
5
6
7
8
9
10




2.

1
2
3
4
5
6
7
8
10
Done
 

LucaMs

Expert
Licensed User
Longtime User
Luca you need to think again
I'm trying right now (2 am!)

First "project":
B4X:
'non-ui project!!!
Sub AppStart (Args() As String)
   DoSomething
   StartMessageLoop
   Log("done!")
End Sub

Sub DoSomething
   For i = 1 To 10
     Log(i)
     Sleep(100)
   Next
   StopMessageLoop
End Sub

1) "app" starts;
2) call DoSomething;
3) first log: 1:
4) Sleep 100 means: "system, do whatever you want, but after 100 (ms? I hope, I have to read again) give me the control again:
5) StartMessageLoop - I thought that this was to be the last statement of AppStart routine, ie that subsequent instructions were not be executed (or at least only after a StopMessageLoop);
6) log done!, because I think that StartMessageLoop takes less than 100ms (but what if not? What if I set Sleep(1)?)
7) log the rest, from 2 to 10, then StopMessageLoop.

So the log should be: 1, done!, 2,3,4,5,6,7,8,9,10.

------------------

Second "project":

B4X:
'non-ui project!!!
Sub AppStart (Args() As String)
   For i = 1 To 10
     Log(i)
     Sleep(100)
   Next
   StartMessageLoop
   Log("done!")
End Sub

1) log 1;
2) Sleep for 100ms, don't stopping the thread (main); but there are no other subs to be executed, then wait 100ms and execute Next.

So the log should be: 1,2,3,4,5,6,7,8,9,10, (StartMessageLoop), done!

[same as @Toley's "result" #79]


[Now I have one neuron only; the other is dead :p]
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Maybe it was an unfair quiz :)

Rules (non-ui projects):
- Code after StartMessageLoop will only be executed after StopMessageLoop is called.
- Events go through the message queue (there are some exceptions to this rule).
- Resumable subs work in the same way that events work. This means that resumable subs will only be resumed when the owner thread (main thread in most cases) processes the message loop.
- A call to Sleep or Wait For always causes the thread to return from the current sub.
- Non-ui programs end after the thread returns from AppStart.

The difference between non-ui programs to ui programs or B4A / B4i programs is that the call to StartMessageLoop happens internally.
Similar to this pseudo code:
B4X:
Sub InternalAppStart
 AppStart(...)
 StartMessageLoop
End Sub

Answer 1:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, done!

Answer 2:
1

Q1.gif



Q2.gif
 

LucaMs

Expert
Licensed User
Longtime User
- Code after StartMessageLoop will only be executed after StopMessageLoop is called.
I thought that this was to be the last statement of AppStart routine, ie that subsequent instructions were not be executed (or at least only after a StopMessageLoop);
... and since I thought this, I wanted tried and it seems that it is not so: a log was executed after StartMessageLoop, I did not wrote StopMessageLoop and I did not stop the server :confused:
 
Top