question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

async lazy val pattern causes infinite suspend

See original GitHub issue

Hi, I’ve just noticed that using new structural coroutines, the code which uses async(start = CoroutineStart.LAZY) { } causes infinite suspend.

As I understand these async(start = CoroutineStart.LAZY) is now treated as a children of TestView object and new coroutine engine waits until these async completes. Shouldn’t LAZY async coroutines be excluded from a wait list if they are not running? Maybe there are more proper way to implement lazy async val?

test example:

class TestView(override val coroutineContext: CoroutineContext) : CoroutineScope {
    val title: Deferred<String> = async(start = CoroutineStart.LAZY) { "demo" }
    val description: Deferred<String> = async(start = CoroutineStart.LAZY) { "desc" }
}

class TestClass {
    suspend fun getView(id: Int): TestView = coroutineScope { TestView(coroutineContext = coroutineContext) }
}

runBlocking {
  val view = TestClass().getView(123)
}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
chrisjenxcommented, Jul 3, 2020

Not sure why this is closed, this shouldn’t break randomly depending on how you write the async.

This suspends forever:

withContext(Dispatchers.IO) {
 async(start= LAZY) {}
}

This works fine:

GlobalScope.async(Dispatchers.IO, start= LAZY) { }

There should not be magic like this - esp considering this has been a problem for nearly two years.

1reaction
LouisCADcommented, May 13, 2019

My current code is the following, does it look right to you?

private class SuspendLazySuspendingImpl<out T>(
    private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
    initializer: suspend () -> T
) {
    private val lazyValue = lazy { GlobalScope.async(dispatcher) { initializer() }}
    suspend operator fun invoke(): T = with(lazyValue) {
        if (isInitialized()) value else withContext(dispatcher) { value }
    }.await()
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

How would I "wrap" this not-quite-"by lazy" result caching ...
You can build a generalized solution by wrapping an async call as follows: ... val fetchToken = lazySuspendFun<String> { suspendCoroutine ...
Read more >
Learn advanced coroutines with Kotlin Flow and LiveData
Creating a new Flow by calling the flow builder or other APIs does not cause any work to execute. The suspending operator collect...
Read more >
F# Async Guide - Medium
This is a usage guide for asynchronous programming in F# using the Async type. The content should be helpful to existing F# Async...
Read more >
repeatedly - clojure.core | ClojureDocs - ClojureDocs
Returns a lazy (infinite!, or length n if supplied) sequence of xs. ... Takes a body of expressions that returns an ISeq or...
Read more >
AsyncLazy - .NET Parallel Programming
I saw AsyncLazy as a simple extension of Lazy which allows to execute an async delegate. In your answer above, it seems logic...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found