32

I want to call blocking a suspend function in a normal function, but does not block Thread to finishing suspend function and then return Response

override fun intercept(chain: Interceptor.Chain): Response {

    // getSession is a suspend function
    val session = sessionProvider.getSession()

    return chain.proceed(
        chain
            .request()
            .newBuilder()
            .addHeader("Authorization", "${session.token}")
            .build()
    )
}
beigirad
  • 4,986
  • 2
  • 29
  • 52
  • 1
    You need to block the thread if you want to return value from your function. If you don't want to block you need to use callback function. – Pawel Jun 01 '19 at 19:56

1 Answers1

67

This looks like you are implementing an OkHttp interceptor, so I am hoping that intercept() is being called on a background thread.

If so, use runBlocking():

override fun intercept(chain: Interceptor.Chain): Response {

    // getSession is a suspend function
    val session = runBlocking { sessionProvider.getSession() }

    return chain.proceed(
        chain
            .request()
            .newBuilder()
            .addHeader("Authorization", "${session.token}")
            .build()
    )
}

runBlocking() will execute the suspend function, blocking the current thread until that work is complete.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    Would this cause issues because the return will be delayed and the intercept function is not marked as a suspend function? – 3366784 Dec 15 '20 at 02:50
  • @3366784: `runBlocking()` is not itself an `suspend` function, so from a compilation standpoint, everything should be fine. How well this works at runtime depends a lot on how well-behaved `sessionProvider.getSession()` is. If that takes a long time, every OkHttp request will take a long time, because the `runBlocking()` call is a blocking call, and `intercept()` will not proceed until `runBlocking()` completes. – CommonsWare Dec 15 '20 at 12:30
  • Okay I understand it depends on `.getSession()`. I was confused because I know there are some languages that will kill your app if a regular function with return value take more than x seconds to return. But in this situation we are forced to delay the return and as you said this is okay. I tested this and added an extra delay and it didn't block the UI thread and didn't crash therefore it should be good. – 3366784 Dec 15 '20 at 21:23
  • @3366784 "languages that will kill your app if a regular function with return value take more than x seconds to return" - could you provide examples? I've never heard of that kind of language feature. – The incredible Jan Jul 19 '21 at 11:42
  • yeah Retrofit by default should run suspend api methods on its predefined background io dispatcher – user924 Aug 16 '21 at 14:50
  • i don't think that we should used runBlocking in production app this is used for testing the app only. – karan May 10 '23 at 09:16
  • @karan: I agree, it is the sort of thing that you should avoid where you can. However, in this case, until and unless OkHttp interceptors start to support `suspend fun` directly, you have little practical choice. – CommonsWare May 10 '23 at 11:05
  • to avoid runBlocking i have used CountDownLatch Example:- GlobalScope.launch(Dispatchers.IO){ request = getToken(chain) countDownLatch.countDown() } try { countDownLatch.await(2000,TimeUnit.SECONDS) }catch (_:Exception){ } – karan May 10 '23 at 11:43
  • @karan: If your objective was the timeout, `withTimeout()` inside `runBlocking()` would be simpler. – CommonsWare May 10 '23 at 16:53
  • @CommonsWare my Objective is not to use runBlocking instead you can use CountDownLatch – karan May 11 '23 at 05:14