问题
I can't use "by lazy" because the callbacks require suspendCoroutine, which borks in android if it blocks the main thread, so I have to use the following "cache the result" pattern over and over.   Is there a way to wrap it in a funButUseCachedResultsIfTheyAlreadyExist pattern to encapsulate the xCached object?  
private var cameraDeviceCached: CameraDevice? = null
private suspend fun cameraDevice(): CameraDevice {
    cameraDeviceCached?.also { return it }
    return suspendCoroutine { cont: Continuation<CameraDevice> ->
        ... deep callbacks with cont.resume(camera) ...
    }.also {
        cameraDeviceCached = it
    }
}
When what I'd really like to write is
private suspend fun cameraDevice(): CameraDevice = theMagicFunction { cont ->
    ... deep callbacks with cont.resume(camera) ...
}
回答1:
You can build a generalized solution by wrapping an async call as follows:
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineStart.LAZY
class LazySuspendFun<out T>(
        scope: CoroutineScope,
        private val block: suspend () -> T
) {
    private val deferred = scope.async(Dispatchers.Unconfined, LAZY) { block() }
    suspend operator fun invoke() = deferred.await()
}
fun <T> CoroutineScope.lazySuspendFun(block: suspend () -> T) = 
        LazySuspendFun(this, block)
This is a simple example of how you can use it. Note that we are able to compose them so that we use a lazy-inited value as a dependency to getting another one:
val fetchToken = lazySuspendFun<String> {
    suspendCoroutine { continuation ->
        Thread {
            info { "Fetching token" }
            sleep(3000)
            info { "Got token" }
            continuation.resume("hodda_")
        }.start()
    }
}
val fetchPosts = lazySuspendFun<List<String>> {
    val token = fetchToken()
    suspendCoroutine { continuation ->
        Thread {
            info { "Fetching posts" }
            sleep(3000)
            info { "Got posts" }
            continuation.resume(listOf("${token}post1", "${token}post2"))
        }
    }
}
On the calling side you must be inside some coroutine context so you can call the suspending functions:
myScope.launch {
   val posts = fetchPosts()
   ...
}
This solution is robust enough that you can concurrently request the value several times and the initializer will run only once.
回答2:
I'll write this as an answer, since it's not possible to post much code in comments.
What you're looking for is something like this:
private suspend fun cameraDevice() = theMagicFunction {
    CameraDevice()
}()
suspend fun theMagicFunction(block: ()->CameraDevice): () -> CameraDevice {
    var cameraDeviceCached: CameraDevice? = null
    return fun(): CameraDevice {
        cameraDeviceCached?.also { return it }
        return suspendCoroutine { cont: Continuation<CameraDevice> ->
            cont.resume(block())
        }.also {
            cameraDeviceCached = it
        }
    }
}
Unfortunately, this will not compile, since closures cannot be suspendable, and neither are local functions.
Best I can suggest, unless I miss a solution there, is to encapsulate this in a class, if this variable bothers you too much.
来源:https://stackoverflow.com/questions/51862715/how-would-i-wrap-this-not-quite-by-lazy-result-caching-function-call-in-idio