Understanding synchronous and asynchronous programming
As in many aspects of life, programming requires clear objectives if success is to be achieved, and those objectives are usually formulated as objectively testable requirements. A set of requirements represents all the characteristics that a software solution must exhibit to be deemed satisfactory, i.e. the things that you must check to accept or reject a solution. They can include functional and non-functional aspects. Functional aspects are directly related to the product definition (‘If I do X, Y happens’) whereas non-functional requirements are not directly related to the solution per se but may be required for other reasons (e.g. ‘Implement using the Cloud to guarantee a certain level of availability’).
For example, in sports or board games there is usually the clear objective of winning a match, and it is usually easy to evaluate whether the player has achieved that objective or not. In basketball, you can see if the ball has passed through the hoop. The shot clock is an example of a non-functional requirement: it’s not necessary for scoring but is an important rule of the game nonetheless that must be complied with to avoid a penalty.
Synchronous programming: chess
A good way to learn how to think in a synchronous and structured way is to solve little chess puzzles. Chess is a ‘complete information’ game, which means that everybody involved in a game has complete awareness of the situation of the game. A chess puzzle is an individual practice mode in which the player must find a solution for an established game situation to finish the game (checkmate). Usually, chess puzzles have an optimal solution which is defined as the solution requiring the fewest moves to reach checkmate.
Important note
If you don’t know the rules of chess, a good introduction is available from the libre/free community-driven server located at https://lichess.org/learn.
The following chess puzzle can be optimally solved by the white player in three moves:

Figure 1.2: A chess puzzle solvable in three moves by white
To solve this kind of problem the player must make their moves sequentially, taking into account the global state of the game (the positions of the pieces on the board), the value of each piece (for in chess each available piece type has a different value), and the potential reactions the opponent may make to the player’s moves. Remember that chess is a turn-based strategy game.
Many problems can be solved in this way, which we refer to as synchronous programming – the decomposition of steps into a cascade having a single line or thread of control. The execution of each step and time of execution are perfectly synchronized, and each step has full information about the global status, variables and available resources.
The following table shows the solution of the previous puzzle in three moves for white. Notice that the flow might change if conditions varied with the opponent’s moves (for example if in Step 1(b) the black player made a mistake):
|
Step |
White (a) |
Black (b) |
|
1 |
Bh8 |
Nd4 |
|
2 |
Qd4 |
Be6 |
|
3 |
Qg7 |
Table 1.1: Solution for the chess puzzle
Asynchronous programming: soccer
A game like soccer is much more complex than chess. It involves two teams composed of 11 players each, and players are assigned positions (goalkeeper, defender, midfielder, forward) which impact their initial locations and potentially their ability to perform certain actions. The overall objective is to score (cause the ball to cross the opponent’s goal line). Any player can do this, and although at any given moment one team is defending and the other is attacking, the roles are fluid and continuous and ‘turns’ at shooting to goal are often unexpected.
The nature of the game allows for an infinite number of strategies. Usually, a team’s strategy involves not only retaining the ball but also making teammates run to distract the opposing team and to gradually occupy favorable ‘real estate’ on the field to improve the chances of any given shot.
The following three diagrams show a typical soccer play in which a defender takes control of the ball and after three moves scores a goal:

Figure 1.3: A soccer play starts with number 2 making a pass to number 10
The main execution timeline is always the one in which the ball is involved. Here it starts with player 2 taking control of the ball and making a pass to player 10, but once the pass is executed player 2 starts to run to a new position.

Figure 1.4: Second move: a dribble by number 10 and a run by number 2
At the second instant in time, multiple things are happening: player 10 dribbles past an opponent, while players 9, 2, and 11 move downfield for better positioning.

Figure 1.5: Third move: number 2 scores
At the third instant, player 10 waits until player 2 is in position to score, after which he passes the ball and player 2 is able to hit the net. The main execution line (scoring the goal) cannot be achieved if the supporting, parallel executions by multiple players are not completed.
Note
The previous example is an adaptation of a real play executed by the Slovenian national soccer team in the 2024 UEFA European Football Championship, the match report for which is available at https://www.uefa.com.
In the same way, asynchronous programming is a technique in which some of an algorithm’s steps are executed in different lines of control than the main one, and those executions may occur simultaneously. Simultaneous execution is usually managed by the operating system or the programming language runtime, but simultaneous execution is not a requirement for asynchronous programming: asynchronous operations can also be run sequentially if desired.
It’s important to note that the multiple control lines in asynchronous programming don’t block each other. As in soccer, individual operations (the equivalent of players in soccer) are free to run unimpeded as other actions occur around them.
We have used the idea of control line in both examples without a formal definition. This is because there are several ways that modern computers control the execution of programs, depending on hardware characteristics, scheduling algorithms, memory management, and I/O handling. Moreover, programming languages and frameworks have their own approaches to concurrency that may vary depending on OS or hardware constraints.
In this section, we have introduced three key concepts which will be developed throughout the book: synchronous solutions, asynchronous solutions, and lines of control. Those concepts will be further elaborated in the specific context of computer science in the following section, to help you move from intuition and sports metaphors to real computer programming.