Difference between using `invokeOnCompletion` and `try-catch` - kotlin

As the title implies, I am curious if there's any difference between doing this;
fun main() {
val job = GlobalScope.launch(Dispatchers.Main) {
withTimeout(2000L) {
delayMe()
}
}
job.invokeOnCompletion { cause -> println("We were canceled due to $cause") }
}
suspend fun delayMe() {
withContext(Dispatchers.Default) {
delay(5000L)
}
}
or this;
fun main() {
GlobalScope.launch(Dispatchers.Main) {
try {
withTimeout(2000L) {
delayMe()
}
} catch(cause: Exception){
println("We were canceled due to $cause")
}
}
...
}
...
in terms of handling exceptions inside coroutines.
PS: The sample code above is inspired from here.

Related

Inappropriate blocking method call warning within a separate Dispatchers.IO block

Why does the first code snippet produces Inappropriate blocking method call warning but not the second one?
private fun prepareList() = launch {
withContext(Dispatchers.IO) {
requireContext().openFileOutput(listFileName, Application.MODE_PRIVATE).use { out ->
requireContext().assets.open(listFileName).use {
it.copyTo(out)
}
}
}
}
private fun prepareList() = launch(Dispatchers.IO) {
requireContext().openFileOutput(listFileName, Application.MODE_PRIVATE).use { out ->
requireContext().assets.open(listFileName).use {
it.copyTo(out)
}
}
}

vertx-lang-kotlin-coroutines test failed

I have been writing a test class:
class TestVerticle {
#BeforeEach
fun deploy_verticle(vertx: Vertx, testContext: VertxTestContext) {
vertx.deployVerticle(Verticle(), testContext.completing())
}
#Test
fun test(vertx: Vertx, testContext: VertxTestContext) {
testContext.verify {
GlobalScope.launch(vertx.dispatcher()) {
val reply = vertx.eventBus().requestAwait<Long>(AVIOEXTDMZAddr, "1")
assert(reply.body() == 1010L)
testContext.completeNow()
}
}
}
}
If the method start() of Verticle is written in the "common" way, the Test is passed positively:
override suspend fun start() {
vertx.eventBus().consumer<String>(AVIOEXTDMZAddr){
it.reply(1010L)
}
}
Differently, if I implement a different solution, with the use of vertx-lang-kotlin-coroutines API, the test throws a java.util.concurrent.TimeoutException
override suspend fun start() {
val consumerChannel = vertx.eventBus().consumer<String>(AVIOEXTDMZAddr).toChannel(vertx)
for (msg in consumerChannel) {
msg.reply(1010L)
}
}
what am I doing wrong?
Loop on channel blocks the coroutine. In this case, it blocks start of your verticle.
Wrap your for loop in launch block:
async {
for (msg in consumerChannel) {
msg.reply(1010L)
}
}
}

Handling hundreds of routes in Vert.x best practices

Please have a look at the piece of code below. Now suppose i'll have hundreds of entity like "person". How would you code such a thing to get it clean, concise, efficient, well structured ? Tx
class HttpEntryPoint : CoroutineVerticle() {
private suspend fun person(r: RoutingContext) {
val res = vertx.eventBus().requestAwait<String>("/person/:id", "1").body()
r.response().end(res)
}
override suspend fun start() {
val router = Router.router(vertx)
router.get("/person/:id").coroutineHandler { ctx -> person(ctx) }
vertx.createHttpServer()
.requestHandler(router)
.listenAwait(config.getInteger("http.port", 8080))
}
fun Route.coroutineHandler(fn: suspend (RoutingContext) -> Unit) {
handler { ctx ->
launch(ctx.vertx().dispatcher()) {
try {
fn(ctx)
} catch (e: Exception) {
e.printStackTrace()
ctx.fail(e)
}
}
}
}
}
You're looking for subrouter.
https://vertx.io/docs/vertx-web/java/#_sub_routers
From the top of my head:
override suspend fun start() {
router.mountSubrouter("/person", personRouter(vertx))
// x100 if you'd like
}
Then in your PersonRouter.kt:
fun personRouter(vertx: Vertx): Router {
val router = Router.router(vertx)
router.get("/:id").coroutineHandler { ctx -> person(ctx) }
// More endpoints
return router
}

How to execute a defined function after each function in Kotlin

I am writing Espresso unit test code.
What I want to do is taking screenshot on every actions without specifying
takeSpoonScreenshot("")
This is my AndroidJUnit4 Testcode:
#Test
fun givenVideoDetail_whenChooseCurrentItem_thenShowCountLabel() {
pickerPage {
clickFirstVideoItem()
}
videoDetailPage {
clickSelectCheckBox()
assertCountLabel()
}
}
and this is my VideoDetailPage.kt:
fun videoDetailPage(func: VideoDetailPage.() -> Unit) = VideoDetailPage.apply {
assertFirstPage()
func()
}
fun screenshotAfterAction(func: VideoDetailPage.() -> Unit) = VideoDetailPage.apply {
func()
takeSpoonScreenshot("")
}
object VideoDetailPage : BaseActions() {
// Write 'How to test' here
fun assertFirstPage() {
resourceIsDisplayed(R.id.send_balloon_image)
resourceIsDisplayed(R.id.media_detail_item_check_box)
resourceIsDisplayed(R.id.video_editor_mute_btn)
}
fun clickFilterButton() = takeScreenshotAfterFunction {
clickButton(R.id.image_editor_filter)
}
fun clickSelectCheckBox() {
clickButton(R.id.media_detail_item_check_box)
}
fun assertFilterSelectionListIsOpen() {
resourceIsDisplayed(R.id.media_filter_list)
}
fun assertCountLabel() {
resourceIsDisplayed(R.id.media_editor_selected_count)
}
}
See that I made takeScreenshotAfterFunction, but It is not proper because I should write takeScreenshotAfterFunction N times.

Observable from merged Observable.just and Subject emits nothing

i have a chain of calls from a presenter to repository which returns an observable. This is the code:
Presenter:
private fun getCategories() =
compositeDisposable.add(
categoriesUseCase.getCategories()
.timeout(TIMEOUT, TIMEOUT_UNIT)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleCategories, this::handleCategoriesTimeout)
)
This is the usecase:
fun getCategories(): Observable<List<Category>> =
repository.getCategories()
.map { it.map { Category(it.id, it.text, it.icon) } }
This is the repo: //subject is BehaviorSubject.create()
fun getcategories(): Observable<List<DiscoverabilityCategoryElement>> =
Observable.just(storage.getCategories())
.mergeWith { subject.flatMapIterable { it.categories }.publish() }
.subscribeOn(Schedulers.io())
.doOnNext { Logger.d("Data", "next categories $it") }
.filter { it.isPresent }
.map { it.get() }
.take(1)
.doOnSubscribe { Logger.d("Data", "Subcribed categories") }
fun saveApiResult(response: Response) {//This is being called after subscribe
subject.onNext(response.categories)
subject.onComplete()
}
Method on storage will always return Optional.empty() (Meanwhile i'm developing)
My problem is, even seeing that subject.onNext is being called, that value never comes to the presenter, i've debug a bit and subject always returns false to hasObservables, maybe i'm losing my observer in some point?
Why do you call publish() on that line? It returns a ConnectableObservable which does nothing until connect is called. However, there is nothing on that line that would require sharing.
Try this:
fun getcategories(): Observable<List<DiscoverabilityCategoryElement>> =
Observable.just(storage.getCategories())
.mergeWith { subject.flatMapIterable { it.categories } } // <-------------
.subscribeOn(Schedulers.io())
.doOnNext { Logger.d("Data", "next categories $it") }
.filter { it.isPresent }
.map { it.get() }
.take(1)
.doOnSubscribe { Logger.d("Data", "Subcribed categories") }
Solution was change
.mergeWith { subject.flatMapIterable { it.categories }.publish() }
by
.mergeWith(subject.flatMap({ rootElement -> Observable.fromArray(element.categories.toOptional()) }))