代码之家  ›  专栏  ›  技术社区  ›  Expert wanna be

具有返回值的Kotlin协程

  •  58
  • Expert wanna be  · 技术社区  · 7 年前

    我想创建一个具有返回值的协程方法。

    例如)

    fun funA() = async(CommonPool) {
        return 1
    }
    
    fun funB() = async(CommonPool) {
        return 2
    }
    
    fun sum() {
        launch {
            val total = funA().await() + funB().await()
        }
    }
    

    如果我想用求和法返回总计,我应该怎么做?

    喜欢

    fun sum(): Int {
        launch {
            val total = funA().await() + funB().await()
        }   
    
        return total
    }
    
    7 回复  |  直到 6 年前
        1
  •  63
  •   Ahmad Ogen    4 年前

    准确返回 Int ,你需要走出协同工作的世界,这就是为什么 runBlocking 用于:

    fun sum(): Int = runBlocking {
        funA().await() + funB().await()
    }
    

    看见 Bridging blocking and non-blocking worlds 在《协作路线指南》中,以及 Composing suspending functions sum 在…内 协同程序。

        2
  •  13
  •   Aleksei Mulin    6 年前

    回答这个问题可能已经晚了,但希望有人会发现它有用。下面的代码段计算3个值A+B+C的总和。每个值在其自己的后台线程中并行独立计算,然后所有临时结果合并为一个最终结果,并返回到主线程以在屏幕上显示。

    因此,计算最终值需要5秒(不是10秒=2+3+5),结果显然是6,并且它是非阻塞的,当sum()执行未完成时,主线程可以处理其他事件。

    suspend fun sum(scheduler: ThreadPoolExecutor): Int = coroutineScope {
    
        withContext(scheduler.asCoroutineDispatcher()) {
            val a = async { funA() }
            val b = async { funB() }
            val c = async { funC() }
    
            a.await() + b.await() + c.await()
        }
    }
    
    fun funA(): Int {
        Thread.sleep(2000L)
        return 1
    }
    
    fun funB(): Int {
        Thread.sleep(3000L)
        return 2
    }
    
    fun funC(): Int {
        Thread.sleep(5000L)
        return 3
    }
    
    class MainActivity : AppCompatActivity(), View.OnClickListener {
        private val tripletsPool = ThreadPoolExecutor(3, 3, 5L, TimeUnit.SECONDS, LinkedBlockingQueue())
    
       ...
    
        override fun onClick(view: View?) {
            if (view == null) {
                return
            }
    
            when (view.id) {
                R.id.calculate -> {
                    GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT) {
                        progressBar.visibility = View.VISIBLE
                        result.setText("${sum(tripletsPool)}")
                        progressBar.visibility = View.GONE
                    }
                }
            }
        }
    }
    
        3
  •  10
  •   Sahil Chhabra    6 年前

    添加另一种实现方法。

    fun sum(): Int {
        var sum: Int = 0
        runBlocking {
            val jobA = async { funA() }
            val jobB = async { funB() }
            runBlocking{
               sum = jobA.await() + jobB.await()
            }
        }
        return sum
    }
    
    suspend fun funA(): Int {
        return 1
    }
    
    suspend fun funB(): Int {
        return 2
    }
    
        4
  •  8
  •   Cevin Ways    5 年前

    我编辑了你的工作,我把funA和funB改成了一个挂起函数,我为sum运算符创建了一个函数,我调用了main函数,这个例子:

    suspend fun funA(): Int{
        return 1
    }
    
    suspend fun funB(): Int {
        return 2
    }
    fun sum() = runBlocking{
        val resultSum = async { funA.await() + funB.await() }
        return resultSum
    }
    
    fun main() = runBlocking{
        val result = async { sum() }
        println("Your result: ${result.await()}")
    }
    

    希望能有所帮助

        5
  •  7
  •   Joonsoo Cris    2 年前

    这是另一种跑步方式 funA() funB() 并行而不使用 runBlocking .

    fun funA() = CoroutineScope(Dispatchers.Default).async {
        delay(3000)
        return@async 1
    }
    
    fun funB() = CoroutineScope(Dispatchers.Default).async {
        delay(3000)
        return@async 2
    }
    
    fun sum() = CoroutineScope(Dispatchers.Default).async {
        val a = funA()
        val b = funB()
        return@async a.await() + b.await()
    }
    

    sum() 在不堵塞主线的情况下,

    CoroutineScope(Dispatchers.Default).launch { // Use Dispatchers.IO for database or file I/O
        measureTimeMillis {
            Log.d("TAG", "sum=${sum().await()}")
        }.also {
            Log.d("TAG", "Completed in $it ms")
        }
    }
    
        6
  •  1
  •   Kristy Welsh    5 年前

    我是这样做的,当试图从我的房间数据库中删除电话号码时,返回一个布尔值。你可以用同样的模式来完成你想要完成的事情。在我的视图模型中:

    private var parentJob = Job()
    private val coroutineContext: CoroutineContext get() = parentJob + Dispatchers.Main
    private val scope = CoroutineScope(coroutineContext)
    
    suspend fun removePhoneNumber(emailSets: EmailSets, personDetails: PersonDetails) : Boolean  {
        var successReturn = false
        scope.async(Dispatchers.IO) {
            val success = async {removePhoneNumbersAsync(emailSets,personDetails)}
            successReturn = success.await()
    
        }
        return successReturn
    }
    
    fun removePhoneNumbersAsync(emailSets: EmailSets, personDetails : PersonDetails):Boolean {
        var success = false
        try {
            val emailAddressContact = EmailAddressContact(emailSets.databaseId, personDetails.id, personDetails.active)
            repository.deleteEmailAddressContact(emailAddressContact)
            val contact = Contact(personDetails.id, personDetails.personName, personDetails.personPhoneNumber, 0)  
            repository.deleteContact(contact)
            success = true
        } catch (exception: Exception) {
            Timber.e(exception)
        }
        return success
    }
    

    在我的活动中:

    runBlocking {
        if (v.tag != null) {
                val personDetails = v.tag as PersonDetails
                val success  = viewModel.removePhoneNumber(emailSets,personDetails)
                if (success) {
                    val parentView = v.parent as View
                    (parentView as? TableRow)?.visibility = View.GONE
                    val parentViewTable = parentView.parent as ViewGroup
                    (parentViewTable as? TableLayout)
                    parentViewTable.removeView(parentView)
                }
         }
    
    }
    
        7
  •  0
  •   Mena    2 年前

    "doWork()" 这确实适用于协同程序,并直接、简洁地返回该工作的结果,从而能够在不冻结用户界面的情况下更新用户界面。

    因此,基于上述内容:

    首先,我会完全 避免 使用 runBlocking ,因为它最终会阻止UI线程或主线程,直到“doWork()”在您指定的任何其他线程上完成其工作。

    我发现实现您的目标最合适的解决方案是使用Koltin Flow,因为如果您想在将来发布进度,您可以在稍后发布。

    方法如下:

    fun doWork(): Flow<Int> =
            flow {
                //do long work 
               val sum:Int = doCalculations()
               emit(sum)
            }.flowOn(Dispatchers.Default)
    

    在你看来,你称这个函数为:

    launch(Dispatchers.Main){
        doWork().single{
        val my result=it
        //Update your UI
        }
    }
    

    另一个更简单的解决方案:

       suspend fun doWork(): Int =
              withContext(Dispatchers.Default){
                    //do long work 
                   val sum:Int = doCalculations()  
                   return@withContext sum
    }
    

    在你看来,你称这个函数为:

    launch(Dispatchers.Main){
        val my result=doWork()
        //Update your UI
        }
    }