Kotlin中如何等待挂起函数执行完成后再执行其他代码
问题原因
你当前的实现时序错乱核心原因是loadParticlesWithoutSetCall内部通过AppScope.launch启动了一个独立协程,launch是非阻塞的异步启动逻辑,调用loadParticlesWithoutSetCall后会立刻执行后续代码,根本不会等待协程内部的粒子加载逻辑完成。
实现方案
最规范的实现方式是通过挂起函数暴露可等待的执行逻辑,不要在工具方法内部私自启动无法追踪的异步协程:
- 将
loadParticlesWithoutSetCall改为挂起函数,移除内部自启动的AppScope.launch包裹,把加载逻辑直接放在挂起函数作用域内:
suspend fun loadParticlesWithoutSetCall() { // 清空原有内容 particlesElem.removeAll() // 加载粒子逻辑 val loadingElem = particlesElem.loading("Fetching particles ...") val particles: ParticlesData? = try { loader() } catch (t: Throwable) { errorMessage(t) null } finally { particlesElem.remove(loadingElem) toWait = true } particlesDat = particles }
- 将
setParticlePicking中依赖加载结果的逻辑放到协程作用域内执行,协程会默认按顺序执行,loadParticlesWithoutSetCall()执行完成前不会自动运行后续代码:
fun setParticlePicking(particlePicking: ParticlePicking) { // 请替换为和当前组件生命周期绑定的协程作用域,比如Android页面用lifecycleScope、自定义View用viewScope,不要滥用全局AppScope避免内存泄漏 lifecycleScope.launch { loadParticlesWithoutSetCall() // 以下逻辑会严格等待粒子加载完成后执行 manualParticleMarkers?.forEach { imageContainerElem.remove(it) } currentParticlePicking = particlePicking manualParticleMarkers = (particlePicking.pickings[imageID]?.map { val marker = ManualParticleMarker( it.x, it.y, image = imageElem, trueHeight = particlesDat!!.imageHeight, trueWidth = particlesDat!!.imageWidth, h = particlesDat!!.h(0), w = particlesDat!!.w(0), parentElem = this@ParticlesImage.imageContainerElem ) imageContainerElem.add(marker) marker } ?: mutableListOf()) as MutableList<ManualParticleMarker> placeMarkers() } }
兼容写法(不推荐)
如果你必须保留loadParticlesWithoutSetCall内部启动协程的逻辑,可以让函数返回启动协程的Job对象,在调用处对Job执行join()来等待执行完成:
fun loadParticlesWithoutSetCall(): Job { return AppScope.launch { // 原有内部加载逻辑完全不变 particlesElem.removeAll() val loadingElem = particlesElem.loading("Fetching particles ...") val particles: ParticlesData? = try { loader() } catch (t: Throwable) { errorMessage(t) null } finally { particlesElem.remove(loadingElem) toWait = true } particlesDat = particles } } // 调用处 fun setParticlePicking(particlePicking: ParticlePicking) { boundScope.launch { // 等待协程执行完成再走后续逻辑 loadParticlesWithoutSetCall().join() // 后续原有业务逻辑不变 } }
注意:不要滥用全局
AppScope启动和UI/组件生命周期绑定的协程,否则组件销毁后协程仍在后台运行,极易引发内存泄漏、空指针崩溃。
内容的提问来源于stack exchange,提问作者justinm6
相关产品推荐
相关产品推荐

