Martin Gaens
07/09/2022, 5:56 PMfun main() = runBlocking {
val bot = bot {
token = "YOUR_API_KEY"
dispatch {
text {
launch { println("launched") }
bot.sendMessage(ChatId.fromId(message.chat.id), text = text)
}
}
}
bot.startPolling()
}
This code should set a callback for when the bot receives any text message from a user. However, the launch { }
never gets launched. And I don't know why! I know all this Telegram bot stuff might be off-topic but it's related to coroutines and I can't comprehend why it wouldn't work. Can somebody tell me what I'm doing wrong?ephemient
07/09/2022, 6:22 PMstartPolling
is a blocking function, no other coroutines in this thread can run until it returnsMartin Gaens
07/09/2022, 6:23 PMstartPolling
in a coroutine?startPolling
into a launch { }
block doesn't seem to help.Joffrey
07/09/2022, 6:31 PMrunBlocking
holds the main thread, and provides a scope that's an event loop on that thread. If you pass a multithreaded dispatcher to it, it should solve the problemMartin Gaens
07/09/2022, 11:39 PMephemient
07/10/2022, 1:52 AMExecutor
via the usual means and wrap it with .asCoroutineDispatcher()
Martin Gaens
07/10/2022, 7:38 AMbot.startPolling()
inside a thread(start = true) { }
with no luck. Here's the code:
fun log(msg: String?) = println("[${Thread.currentThread().name}] $msg")
fun main() = runBlocking {
val bot = bot {
token = "XXX"
dispatch {
command("sayHi") {
launch(<http://Dispatchers.IO|Dispatchers.IO>) {
log("Hey")
bot.sendMessage(
chatId = ChatId.fromId(message.chat.id),
text = "Hi!"
)
}
}
}
}
launch(Dispatchers.Default) {
log("Started polling...")
bot.startPolling()
}
Unit
}
Also are there any good resources for learning about coroutines? Because when you google "create multithreaded executor", it doesn't bring up anything useful and easy to understand about creating multi-threaded executors.uli
07/10/2022, 2:38 PMlaunch
and see if that does the trick.Joffrey
07/10/2022, 2:45 PMIf you pass a multithreaded dispatcher to it, it should solve the problem@Martin Gaens I meant to pass a dispatcher to
runBlocking
- not to the launch
directly. The thing is, because you run your top-level code in runBlocking
, the thread is blocked while waiting for the second launch
to finish, even if the body of that launch
is run on a different thread. Everything needs to be multithreaded here, not just the body of launch
.runBlocking
at that level. Everything is blocking here, except your own coroutine call in launch
. By capturing `runBlocking`'s scope in the callback's lambda instead of using runBlocking
inside the callback, you're possibly breaking invariants for this callback. Maybe the framework expects you to finish doing your work in the callback before giving control back to the framework. It also probably gives you a separate thread to do that, but you're switching back to the thread held by runBlocking
(which is the main thread in this case).uli
07/10/2022, 6:01 PMfun main() = runBlocking {
val bot = bot {
token = "YOUR_API_KEY"
dispatch {
text {
println("received") // Check if your callback is called at all
launch(<http://Dispatchers.IO|Dispatchers.IO>) { println("launched") } // See if couroutine gets launched
bot.sendMessage(ChatId.fromId(message.chat.id), text = text) // Do not call framework from background thread
}
}
}
bot.startPolling()
}
fun main() {
cal scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
val bot = bot {
token = "YOUR_API_KEY"
dispatch {
text {
println("received") // Check if your callback is called at all
scope.launch { println("launched") } // See if couroutine gets launched
bot.sendMessage(ChatId.fromId(message.chat.id), text = text) // Do not call framework from background thread
}
}
}
bot.startPolling()
}
Martin Gaens
07/10/2022, 8:21 PMIn your Original code, pass a dispatcher toTheand see if that does the trick.launch
<http://Dispatchers.IO|Dispatchers.IO>
dispatcher is not working if I pass it into the original code.
I meant to pass a dispatcher toYeah I also tried that, also not working. @Joffrey thanks for the answer, you're right that the framework expects me to do all the work in the thread, but I don't really like holding onto a thread when I'm simply waiting for a Ktor response. That's why I wanted to go full coroutine-based. But when the framework doesn't support coroutines, I guess my only option to achieve this goal is to make my own... 😛 @uli thanks for your detailed answers! I tried doing a simplerunBlocking
println()
inside a coroutine with no luck. The coroutine simply wasn't called at all. Also, the first thing you proposed didn't work. The second one with the custom scope worked! But it's still very annoying to me that the framework creates threads when coroutines are available to us in Kotlin and are preferable for doing simple network calls. However, why shouldn't I call the framework from a background thread/coroutine?uli
07/10/2022, 8:53 PMMartin Gaens
07/10/2022, 9:10 PMgildor
07/11/2022, 4:37 PM