https://kotlinlang.org logo
#coroutines
Title
# coroutines
p

peekandpoke

05/27/2021, 6:46 AM
Hi there! I am switching to
kotlinx-coroutines 1.5.0
and this now has marked the GlobalScope as a delicate API. I totally understand the reason behind this but I am somewhat stuck now. So my question is: How can I create a
CoroutineScope
that I will then be reusing? I am doing this now:
Copy code
val scope = CoroutineScope(EmptyCoroutineContext)
... and then I reuse this scope. Does this make any sense? And similar in KotlinJS when using Flows we often times have code like this:
Copy code
fun callApi() {
  GlobalScope.launch {
    api.getStuff().collect {  // collect() is a suspend function so it needs a coroutine scope
      ...
    }
  }
}
What would be a clean way to get a reusable coroutine scope here? Thanks in advance!
c

CLOVIS

05/27/2021, 8:02 AM
You shouldn't use
GlobalScope
to launch your API calls: that isn't structured concurrency. If your API has some kind of authentication or whatever, one good solution would be to create a Job in the class that handles authentication, and use that one to
launch
. This way, you have a single object that can cancel all ongoing requests, if you need to.
p

peekandpoke

05/27/2021, 9:50 AM
Thank you for your answers. Good idea @CLOVIS . One last question: How do a get a CoroutineScope if I need one. I want to avoid passing around the scope through the entire application. So is something like creating an internally used scope useful, like:
Copy code
val scope = CoroutineScope(EmptyCoroutineContext)
How will this scope behave?
c

CLOVIS

05/27/2021, 9:52 AM
What I usually do is have each component of the project that can launch stuff have their own scope. So
Client
has a scope,
Authenticator
as well, but pretty much everything doesn't (it just calls suspending function from the client, which use the client's scope). Each ‘scoped' class takes an optional parent job as a parameter, so the main function can declare a job that the whole application then depends on, so the main function can then cancel everything else when it's done with what it has to do
p

peekandpoke

05/27/2021, 9:56 AM
How do you create the scope?
Or how do you obtain the scope? @CLOVIS
c

CLOVIS

05/27/2021, 10:07 AM
I just have a job per ‘scopped class' and call
private val scope = CoroutineScope(job + <whatever dispatcher makes sense here>)
p

peekandpoke

05/27/2021, 10:32 AM
Ok thank you very much! Will try something like this.
s

steamstreet

06/03/2021, 9:02 PM
So I have a ton of component that execute network calls… basically they have access to a GraphQL instance that can be used to grab data for the component. Are you suggesting that each have a scope, and then pass scopes to all of them? This seems like a ton of extra work for a JS application.
I have considered some kind of GraphQL scope, which is a sort of global scope that all of these GraphQL operations would use.
The official docs appear to suggest using
MainScope
which I’m not at all sure what that is. https://play.kotlinlang.org/hands-on/Building%20Web%20Applications%20with%20React%20and%20Kotlin%20JS/08_Using_an_External_REST_API
c

CLOVIS

06/03/2021, 9:10 PM
I don't know GraphQL, so I can't say for sure, but I assume you have some kind of ‘connect' method that connects to the server, then you do some operations with the connection, and then you disconnect when you're done. If so, you can do something like:
Copy code
class Client(..., parent: Job? = null) {
  private val scope = CoroutineScope(SupervisorJob(parent) + <http://Dispatchers.IO|Dispatchers.IO>)
Copy code
// The rest of your class, with a 'send' method or whatever
  // When you need to launch you use the scope
Copy code
fun close() {
    scope.cancel() // kills all ongoing requests
    // Your GraphQL closing logic
    ....
  }
}
Copy code
// In some other file
fun Client.myRequest1(...) {
  ...
  // Uses Client.send, so it uses the client's scope
}
I'm not sure this pattern is recommended by the Kotlin team, but I've used it and it feels quite nice.
7 Views