• 2 months ago
These are all the video quizzes made for JESPROTECH between 2023 and 2024. Enjoy and have a good one everyone!

---

As a short disclaimer, I'd like to mention that I'm not associated or affiliated with any of the brands eventually shown, displayed, or mentioned in this video.

---

All my work and personal interests are also discoverable on other different sites:

- My Website - https://joaofilipesabinoesperancinha.nl/
- Reddit - https://www.reddit.com/user/jesperancinha
- Credly - https://www.credly.com/users/joao-esperancinha/badges
- Pinterest - https://nl.pinterest.com/jesperancinha/
- Instagram - https://www.instagram.com/joaofisaes/
- Facebook - https://www.facebook.com/joaofisaes/
- Spotify - https://open.spotify.com/user/jlnozkcomrxgsaip7yvffpqqm
- Medium - https://medium.com/@jofisaes
- Daily Motion - https://www.dailymotion.com/jofisaes
- Tumblr - https://www.tumblr.com/blog/jofisaes

---

If you have any questions about this video please put a comment in the comment section below and I will be more than happy to help you or discuss any related topic you'd like to discuss.

If you want to discover more about my open-source work please visit me on GitHub at:

- GitHub - https://github.com/jesperancinha
Transcript
00:00Music
00:30Music
00:40Music
00:50Music
01:08Hi everyone, in the last video I asked what would happen if I would try to create a coroutine scope
01:14using a main dispatcher in a simple Java application.
01:19The answer to this question can be a bit convoluted, but the point I'm trying to make is that
01:24if an application does not have a main dispatcher available,
01:28the attempt to use a coroutine scope with a main dispatcher will result in an illegal state exception.
01:37So the answer to that question was D.
01:39Thank you so much for watching this video.
01:41Please subscribe if you like the content that I'm developing.
01:44Give a big thumbs up to this video.
01:46And for now, have a great day.
01:48Fijne dag. Alvast fijn weekend gewenst.
01:51En tot volgende keer.
01:54Doei.
01:56If you'd like to know more about coroutines, please visit my GitHub repo on GitHub.
02:02The links and the shortcuts to it are written in the description below.
02:38Hi, everyone.
03:03In the last video, I made a question about unconfined coroutines.
03:07When we launch a coroutine with an unconfined dispatcher,
03:10the coroutine is not associated with a specific thread,
03:13but all subroutines are launched, and they will run on the same thread.
03:18So this means that they will run concurrently.
03:20So in the example, we ran three coroutines, a parent, which will log master,
03:25and two subcoroutines, one that will print out mouse, and one that will print out cat.
03:32So the correct order is master, mouse, and cat.
03:37Or it could be mouse, master, and cat.
03:40And this is because master and mouse do not wait, they run concurrently.
03:45And cat, of course, has a delay.
03:48And that delay means that it will always run later,
03:52independently that they run concurrently on the same thread.
03:56Thank you for watching. I hope you enjoyed this video.
03:59Please subscribe, and I'll see you next time.
04:01Goodbye.
04:31Hi, everyone.
04:39In the last quiz, I asked a question about unconfined coroutines.
04:43So we've seen in videos before that unconfined coroutines, when they start,
04:50they will start on a random thread.
04:54And when we have nested coroutines, they all start on the same thread as the parent.
04:59When they leave the control of the thread, then they run in a non-blocking way.
05:09And when they try to regain control of the thread, that happens also randomly.
05:14So that control regain of one particular thread is random,
05:21and the thread will be different or the same.
05:27And I was able to reproduce that on case A, not yet on case C.
05:33So the correct answers are A and C.
05:36Hope you enjoyed this video.
05:39Please like if you did, and hit that subscribe button
05:44to not miss out on further videos that I will be posting.
05:48Until then, keep it graceful and stay sharp.
09:51This is the right answer.
10:03The answer is D. If you recall the exercise was this, and the code for this exercise is
10:10this.
10:11Let's run it.
10:13As you can see, we have a hundred coroutines that have just been launched and finished
10:18and the code took about one second to execute. How does this happen? Well, the Dispatchers.io
10:25have a limit of 64 threads where the coroutines can run or the maximum number of
10:32CPUs that you have in your machine if that value is higher than 64. That's what they say
10:39in their documentation right here. If we read here through this code,
10:44you see immediately that they give an example where if they configure parallelism, you might
10:50create all of these threads. However, they say here down below that the maximum threads will
10:57still be the maximum that we've calculated before. So 64 or the maximum number of cores of your
11:03machine. When we run the code with this 100, we're actually not running threads. We are running
11:08coroutines. That's what it says here in the documentation. And when we run our code,
11:15we are causing a delay and that delay is one second for all the 100 threads. But what this
11:20means is that we are just starting the different coroutines and they will be scheduled. They will
11:27all be scheduled at the same time. When they get back, they will also get back at the same time,
11:32but they will run with the logic that they mentioned here, which is the best effort logic,
11:39whatever that logic actually is. Back into the code, that's what generates this very small
11:45overhead here, which then explains why this takes a bit more than one second, but not exactly one
11:51second. This quiz is about predicting the runtime of coroutines. We want to call a coroutine,
12:01catch an exception, send an error report, send a global report, and log the process completion.
12:07You are given four choices and only one of them follows this prediction. Can you guess which one
12:13is it?
12:31Why are coroutines unpredictable? They're not. In this video, I want to explain a bit how the
12:37last exercise was made and what is the goal behind. Coroutines are essentially made to make better
12:44usage of system threads. In broad terms, coroutines were made to make better usage of our system. What
12:50we want to do with coroutines is usually ride the wave of threads that are running on our system.
12:56We want to make sure that we can suspend and resume these coroutines. When they are suspended,
13:04they can give room for another coroutine to use the thread. But what we are also concerned when
13:08we are programming with coroutines is the predictability of our code. In some situations,
13:14we try to make an implementation with our coroutine. We try our best to pick the one
13:18that's better and then we can use that coroutine to make a better use of our system.
13:23We try our best to pick the one that's better and many times, more often than not,
13:27it seems like it fails when we try to pick up a scope or we try to get a good cancellation policy
13:37with our coroutines. We just want to be able to predict what kind of result are we going to have.
13:42This exercise was made a bit for us to become more aware of how that works.
13:47Let's get into the exercise and find out which one of the options A, B, C or D are the correct
13:53one. This is the main program which is something you cannot see in the exercise itself. In the
13:58exercise, we don't really see the full implementation of the program, just the
14:02methods that are being called. So here we have the four different calls to the different options
14:08that we want to investigate and see if they match with what we want to achieve. To illustrate our
14:14problem and to show which option was the correct one, I've created a small example which is a bit
14:20of associated how cheese is made. So we're going to have these different processes. We're going to
14:27have a machine start, cheese curd making, let it sit and a make report. Super simple, this is not
14:34how cheeses are made but this is a way to kind of show how this works. The first example is test
14:43launch and this is representative of option A of our quiz. When we started, we are first running a
14:49coroutine in a blocking scope, so there isn't really a difference to our runtime at this point
14:54and this coroutine is going to launch another coroutine in the global scope. The global scope
15:01here is a bit of a trap in all of the different options, doesn't really have an effect in the
15:06outcome of what we are trying to achieve. The first log that we get is that the machine has started
15:11and then afterwards we're going to run another coroutine and this coroutine we start in the
15:19IO context and there we will start the cheese curd making process. Until this point we've
15:27achieved already two things, we've started a machine, we've made the curds and now we're going
15:32to launch another coroutine which is going to let the curd sit for a while and then we're going to
15:38generate our exception. So this is the point where the exceptions are going to be generated
15:43as an equivalent to what we have in our quiz. In this case we're going to join the job that is
15:48running on IO and what this means is that the IO coroutine will be suspended until this job has
15:56reached its completion and then it will resume. But we have already generated an exception so the
16:02exception has already been, because of this join, thrown to the console and that also means that
16:08there will be no on failure being caught and because of that this launch here which is what
16:13we want to launch to create a report has already launched in the meantime so this means that we
16:17already sent the report, the global report, after the exception has been generated and then finally
16:23we just wait for the process to be done and then we say the process is complete. But here the order
16:27has changed and it goes against our expectations. So let's run this to have a visualization of
16:33what I mean with this. So we start our process, now we should see that the machine has started,
16:40we started the curd making, now we're going to let our cheese sit. Here at this point
16:45an exception will be generated but instead of waiting for the exception to be generated
16:53we already sent the report and of course then comes the exception to the console
16:57and then we see that the exception has been thrown to the console but this is the original
17:02exception. Though the report exception, this one here, an error has been reported, we don't see
17:07anywhere in the console and the reason is because it has not been called. What is happening is that
17:12we didn't even call the error report, so we did not send the error report at all. So it's definitely
17:18not option A and just to be very quick about it, so we've created a blocking scope then inside of it
17:25a global scope which should cater to these two different coroutines, one in dispatchers.io and
17:31another one in the same global scope. This runs smoothly until we generate the exception
17:37which then would be caught here but because we are joining here and suspending the coroutine
17:41dispatched by the io dispatcher we are not suspending the global scope coroutine and so
17:49it keeps running and it completes the report before the error report has ever been generated.
17:56So this invalidates option A. If we go to option B, so then we come to option B. In option B I
18:02gave a function that I called it test remove and in the test remove what I mean by test remove is
18:09that we are not going to use separate coroutines, so this is a bit of a reference to the first
18:15test that we made for option A. We are only using one context that's going to be shared in all of
18:21the nested coroutines and so we start in the same way as for option A. We start with the blocking
18:30scope and then we go and we launch other coroutines with the global scope and in this case
18:37we are still making launches, so we are still creating jobs. We are also calling here a join.
18:44The difference here is that everything is part of the same context, so when an exception is
18:51generated here and we have the join here, we are going to be able to pick up that this exception
18:56and we're going to be able to report the exception. What we are not going to be able to do is to call
19:01this launch. The reason being is because they are part of the global scope and we are making this
19:06launch here and we are making a join here, this also means that nothing will continue, so this
19:14coroutine will not be able to continue until this coroutine is completed. As we give a report,
19:21so we say an error has been reported, what also happens is that this launch never finds its place
19:27because an exception has been thrown and therefore we go straight up to here and then we will get to
19:33the completion of our process. Let's see if this is indeed what happens. So let's launch the
19:38application again with all the other functions commented out and let's start and see what
19:46happens. So as we can see here we started our process, we have a machine start, we have the
19:52curds and then we're going to go straight into where the errors is happening. We see here in
19:59yellow letters that we were able to catch the exception and we were able to report it,
20:03it also gets reported to the console and then it goes straight to process has been completed.
20:09And this explains the difference between what's happening here and what's happening here because
20:16here we have a different scope and because we have different scopes, these two launches will
20:23happen, have run in parallel and although the whole nesting is affected by cancellation,
20:31cancellation happens in this case after the code has ran and after the global report has been sent
20:38which is what happens here. So that's the main difference between the two of them,
20:43so in one case the order is not the one that we expect and in the second case
20:51we simply just don't get the complete report, so A and B are not valid. Let's go to option C,
20:58let's comment the other options out and we're going to test async. So the async test is similar
21:06to option A, the difference here is that we are going to launch an asynchronous
21:15coroutine and by doing this we are going to create a deferred object which is something that we get
21:21here. This is a deferred object so I can show it to you in the code so if we make this a variable
21:31we see that we actually have here if we specify type explicitly we have a deferred
21:39and a deferred allows us to use an await and the await allows us to get back the return object for
21:48this function but we are not doing that here we're just launching a coroutine and we are generating
21:54an exception. When we generate this exception since this coroutine has been launched asynchronously
21:59and we're not waiting for it to return the exception simply just disappears, we don't get
22:04it in the logs and we are not able to catch it. So this run catching here is completely useless
22:11so we will not report the exception and we will jump straight into the report.
22:17So this one will generate a very simple console output and the exception
22:24will not be observable in the logs. Let's start this test as well, let's run it and as you can see
22:32we start the machine, we start the cheese carts, we make the report and we let it sit.
22:40It's also curious and something that I didn't mention yet is that as we make this test
22:47we see that we will we see that of course we have two different coroutine scopes one is
22:53dispatches io and the other one is launch and they are being launched and these two run in parallel.
22:58What we see here happening is that the let it sit happened after the global report has been sent
23:04so this is definitely also not a good option. A, B and C are not the options we are looking for
23:10and of course by exclusion we can also deduct that option D is the correct one but let's see why is
23:16that. We comment all of the other options out and we're going to go to the option D
23:23which is the async and wait solution. The async and wait solution what it does is essentially a
23:29bit more than what the option C was doing however here we are just adding an await and by doing an
23:37await we are causing this whole coroutine we are causing this deferred job to wait for a return
23:47value in this case our return value will just be if I'm correct it will be a job and why is it a
23:58job very simple the job comes out of the launch so this launch is the reason why we have a deferred
24:05for for type job so here we are going to await for that return value and as we await for the
24:11return value nothing here will be launched which now starts to match with what we want
24:18because we are waiting for the return result we are also waiting for the exception to be caught
24:22so we will get the exception and we will report it so that's what we have here report exception
24:28and then finally we're going to launch the global report and at the end we're going to complete the
24:33process so this is our correct option let's run it and see how it behaves in terms of the console
24:40there we go so we see that the machine starts we get the cheese crud we let it sit then
24:47something happens and an error has been created will be created it throws the exception the
24:52exception gets caught the error has been reported we get the exception an error has been generated
24:58and finally we make the global report and at the end we complete the process so this is why
25:03option d is the correct one now if we look at the four different options they are all running
25:09examples and they all highlight the difference of when and how should we use an async and
25:16a launch and how coroutines behave when we change their context it doesn't it doesn't give you the
25:22full perspective of how coroutines actually work with the different with the different context and
25:28different scopes or how the content or how the context get transfer gets transferred between
25:34nested coroutines but it sure does highlights the difference between using async launch
25:42and everything here that we have seen is also an example of something that coroutines is called
25:47structured concurrency and structured concurrency is something that coroutines offer out of the box
25:54and it is this whole story about putting one coroutine inside another coroutine inside another
25:58coroutine and having either deferreds or jobs working for us together it offers a glimpse about
26:07cancellation but that is something for another video for this video i only want to say that
26:13i hope you enjoyed it i hope you enjoyed the doing the questionnaire i hope you were able to
26:18provide the correct answer and if not i hope with this video i was able to help you
26:22with questions you may have about conflict routines structured concurrency
26:26jobs deferreds async launch and uh if you enjoyed this video please give it a big thumbs up and if
26:35you've been enjoying my channel so far please subscribe that will be i will be posting more
26:41videos soon and thank you so much for watching and until the next video keep it graceful and stay sharp
27:00t is a covariant type u is a contravariant type and v is an invariant type v and t
27:08are being used in a higher order function and they are marked with the unsafe variance annotation
27:15this quiz is to think about when would we ever need to use the unsafe variance annotation
27:39so
27:50let me answer the quiz regarding the unsafe variance annotation the unsafe variance is
27:55something that it is reported since the beta version of kotlin 1.0 and it is described in this
28:01blog to be something that should only be used as a last resource and only if we want to give
28:08specific functions another functionality that we originally intended for their types
28:12if we look at the implementation of the list interface in the kotlin sdk we see that although
28:16list is covariant to type t and as a result placed in the out because we want to preserve functions
28:22like index of then we make for them the exception and obliterate the effects of using in and out
28:30by using the annotation unsafe variance that way we don't need to worry about if it's input or
28:36output we can use type t also as contravariant and that way we keep list as immutable as it is
28:42intended to be but we still have to provide input parameters in order for it to give us back the
28:48index that we need it's quiz time which of these is our possible return forms this is the function
29:06option a return one option b return one option c return just return option d
29:15return news option e return null option f news option g return dollar latest news
29:27and finally option h return latest news in 5 4 3 2 1 0 the return type is specified to be a string
29:41and we are inside the scope of a function within the curly brackets we have to use the return
29:45keyword we are returning a non-null value and although on h we don't return the content of
29:51latest news we are still returning a string the correct answers are b d g and h
30:51do
31:08what is the result of the following we call with with maincoon and the function with print line
31:15this and print line length so which one is the right option is it a an exception because nothing
31:22will print b maincoon c maincoon and the number 10 or d this and length as literals
31:35the answer is c we call the function with with two parameters the first parameter is maincoon
31:41as a string and the second parameter is a function literal with a receiver in the function definition
31:46t is the receiver and r is the return value block is the function that we define between curly
31:52brackets we can also specify the types when you call with and the two arguments can be placed
31:56between parentheses although not necessary when the last argument is defined as a function if you
32:01enjoy software development quizzes please subscribe for more videos
32:09quiz time semantics in kotlin
32:14five concepts to know about kotlin
32:18which of these is the named function
32:23which one of them is the lambda expression
32:28how about the anonymous function which one is it
32:31and finally which one of these are the function literal
32:49so why should we use anonymous functions when we have lambda expression
32:52anonymous functions allow us to define the output type in the function literal itself
34:52so
35:22so
35:41as a short disclaimer i'd like to mention that i'm not associated or affiliated with
35:45any of the brands eventually shown displayed or mentioned in this video

Recommended