The main idea is that all actions that affect the UI happen sequentially. Some examples of these kind of actions could be changing the text of a label, or handling users interacting with the app via a button or a text box.
Sequential execution in App Inventor means that different blocks can never interrupt each other.
Different blocks and handlers will always be executed in sequence. Once a block starts executing, it
will execute till the end, no matter if there are other blocks reacting to the one in progress, or users pressing buttons in the app. On more technical terms, we say that everything in the UI is executed in one Thread, the UI Thread, which is responsible for all updates and interaction that happen in each Screen of your app. For
this to happen, we need some kind of mechanism to keep track of what needs to be done next, and when something should be done. A very simple model would be a Queue, just like the one you join at the supermarket to pay for your shopping. Tasks can be added to a Queue in the same way that people join in the queue from the end of it.
With all this in mind, let's see the example in the documentation. (From How to add a component )
“This
gives a simple execution model for users: An App Inventor procedure
will never be interrupted by an event handler, or vice versa; nor
will one event handler be interrupted by another. This is illustrated
in Figure 1:” (Ellen Spertus)
|
The example uses two Ball
components, one CollidedWith handler that should fire as
soon as Ball1 collides with anything, and an extra
procedure called wait, which we don't really see what
it does, but we are going to suppose that it takes 20 seconds to
execute (imagine that it is loading content from the internet and it
takes that long). All the code, except for the collision handler, is
contained within a Click handler.
When Button1 is clicked,
the two balls are positioned in exactly the same place, first Ball1,
and then Ball2. Positioning the second ball in the same place as
Ball1 should trigger the collision handler for Ball1. Some users
would expect this to happen straight away, but if you were to execute
this code, you would see that the collide handler will not execute
until the click block finishes, and this would take at least 20
seconds because of the call to wait.
Following the same idea as above, you
can think about the UI thread as if it was a Queue to get into the
cinema; on arrival, ticket buyers will position themselves at the end
of the queue.
When Button1 is clicked, we add the
following things to the Queue:
- position Ball1
- position Ball2
- wait
- set Label1 text to : '...End of Button1.click...'
When Ball2 is positioned, the
CollideWith handler is added to the Queue, but it cannot be executed
straight away because the UI thread still has some tasks to do. So,
after a couple of seconds we will see a queue such as:
- position Ball1 (DONE)
- position Ball2 (DONE)
- wait (IN PROGRESS)
- set Label1 to: '… End of Button1.click ...' (TO DO)
- CollidedWith (added to the queue when the collision happens)
- set Label1 text to: '… Ball1.CollidedWith ...' (TO DO)
So Ball2 makes the handle trigger, but
that does not mean that it will be executed straight away, instead it
means that it will be added to the queue in the UI thread.
In the end, Label1 will always be
assigned the value '...End of Button1.click...' first, and the value
'...Ball1.CollidedWith...' later.
When both blocks finish, it will always read: “...End of Button1.Click......Ball1.CollidedWith...”.
When both blocks finish, it will always read: “...End of Button1.Click......Ball1.CollidedWith...”.
Please note that having a procedure
like wait blocking the UI is a really bad idea
in an app, because as we saw in the explanation above, the app will
be unresponsive for those 20 seconds, and this is not a pleasant
experience for the users. If your app needs to do some expensive
processing, it is quite possible that this interaction will block the
app for as long as it takes.
From a design and development point of
view, it is also important to note that certain blocks in App
Inventor were designed with the UI thread in mind, and broken down
into two different stages. For instance, the Web component Get
call could be considered part 1 of a call to a web resource. This
call will not block the UI because it is not performed in the UI Thread. When the response from the Get
call is received, then part 2 will execute, which would be the event
GotText (or GotFile, depending on how Get
was configured). If you are planning on developing a new component
for App Inventor, you need to take this into account.
Also related to this idea of sequential
execution, a lot of users ask why their timers do not fire on
time, for instance exactly every 2 seconds. Actually the timers
fire at the time they are supposed to, but this only means that their
handlers get added to the scheduling queue for UI updates. If at that
particular moment something else is executing in the Screen, then the
actions fired by the timer will have to wait for those other actions
to finish, before they can be triggered. But once they get next to be executed in the Queue, they will fire one after the other, not waiting for any intervals. The waiting was already done before joining the Queue.
This is actually something very usual
in UI libraries, from Java (Swing) to Android, and even JavaScript.
The browser, in which JavaScript executes, also has a single thread
of execution, generally called the event loop, and it will
behave in the same way as App Inventor does. Same happens if you run JavaScript on the server through something like node.js.
This is all a simplification of what
really happens, and Android can create other Threads of execution
that do not conflict with the UI thread to carry on with other
operations. For a deeper dig in the matter, follow the link in the Android documentation to the Painless Threading article.