如何使Coroutines仅在第一次完成后才启动下一份工作

发布于 2025-01-30 15:12:59 字数 1140 浏览 1 评论 0原文

假设我有一个按钮,它在Android中有一个侦听器:

someView.setOnClickListener {
    viewModel.doSomething()
}

每次按下按钮时,这都会创建一个新工作。在我的ViewModel中,Dosothing函数正在从ViewModelScope启动暂停函数:

fun doSomething() {
  viewModelScope.launch {
       doSomething1()
       doSomething2()
       doSomething3() 
   }
}

suspend fun doSomething1() {
    delay(100)
    Log.d("TEST", "doSomething1: 1 ")
}

suspend fun doSomething2() {
    delay(300)
    Log.d("TEST", "doSomething2: 2 ")
}

fun doSomething3() {
    Log.d("TEST", "doSomething3: 3 ")
}

现在,如果此按钮连续地按非常快速按下(从理论上讲,我可以两次从听众中调用该功能,以便执行第一个呼叫尚未完成),我将在我的logcat中得到以下结果:

  d/test:Dosomething 1:1
D/测试:Dosomething 1:1
D/测试:Dosomething 2:2
D/测试:Dosomething 3:3
D/测试:Dosomething 2:2
D/测试:Dosomething 3:3
 

我实际想实现的是,如果我可以从同一范围启动dosomething(),将会发生什么,因此它运行同步。

  d/test:Dosomething 1:1
D/测试:Dosomething 1:2
D/测试:Dosomething 2:3
D/测试:Dosomething 3:1
D/测试:Dosomething 2:2
D/测试:Dosomething 3:3
 

我该如何实现这种行为,以便在开始同一个Coroutine之前,必须完成第一个行为?

Lets say that I have a button, which has a listener in Android:

someView.setOnClickListener {
    viewModel.doSomething()
}

This will create a new Job for each time that the button is pressed. In my ViewModel, the doSomething function is launching suspend functions from the viewModelScope:

fun doSomething() {
  viewModelScope.launch {
       doSomething1()
       doSomething2()
       doSomething3() 
   }
}

suspend fun doSomething1() {
    delay(100)
    Log.d("TEST", "doSomething1: 1 ")
}

suspend fun doSomething2() {
    delay(300)
    Log.d("TEST", "doSomething2: 2 ")
}

fun doSomething3() {
    Log.d("TEST", "doSomething3: 3 ")
}

Now, if this button gets pressed very quickly in succession (in theory, lets say i can call the function from the listener twice, so that the execution of the first call hasn't finished yet), I will get following result in my logcat:

D/TEST: doSomething1: 1
D/TEST: doSomething1: 1
D/TEST: doSomething2: 2
D/TEST: doSomething3: 3
D/TEST: doSomething2: 2
D/TEST: doSomething3: 3

What I actually want to achieve is, what would happen if I could launch doSomething() twice from the same scope, so it runs synchronous.

D/TEST: doSomething1: 1
D/TEST: doSomething1: 2
D/TEST: doSomething2: 3
D/TEST: doSomething3: 1
D/TEST: doSomething2: 2
D/TEST: doSomething3: 3

How can I achieve that behaviour, so that before starting the same coroutine, the first one has to finish?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

水晶透心 2025-02-06 15:12:59

您可以使用MUTEX锁来执行此操作 - 无论您按下多少次按钮,锁定锁定序列都无法启动直到完成。

private val lock = Mutex()

fun doSomething() {
  viewModelScope.launch {
       // lock means only one "1-2-3" sequence can execute 
       // at a time, subsequent calls will suspend here and wait
       // for the lock to be released before starting
       lock.withLock {
           doSomething1()
           doSomething2()
           doSomething3() 
       }
   }
}

您也可以使用频道一次运行一个频道

private val channel = Channel<Job>(capacity = Channel.UNLIMITED).apply {
    viewModelScope.launch {
        consumeEach { it.join() }
    }
}

fun doSomething() {
    channel.trySend(
        // send a lazily executed "1-2-3" job to the channel for it
        // to run (will run jobs one at a time and wait for each
        // job to complete before starting the next)
        viewModelScope.launch(start = CoroutineStart.LAZY) {
            doSomething1()
            doSomething2()
            doSomething3() 
        } 
    )
}

You can use a Mutex lock to do this - the lock would keep subsequent 1-2-3 sequences from launching until the prior ones are complete, no matter how many times you push the button.

private val lock = Mutex()

fun doSomething() {
  viewModelScope.launch {
       // lock means only one "1-2-3" sequence can execute 
       // at a time, subsequent calls will suspend here and wait
       // for the lock to be released before starting
       lock.withLock {
           doSomething1()
           doSomething2()
           doSomething3() 
       }
   }
}

You could also use a channel to run them one at a time

private val channel = Channel<Job>(capacity = Channel.UNLIMITED).apply {
    viewModelScope.launch {
        consumeEach { it.join() }
    }
}

fun doSomething() {
    channel.trySend(
        // send a lazily executed "1-2-3" job to the channel for it
        // to run (will run jobs one at a time and wait for each
        // job to complete before starting the next)
        viewModelScope.launch(start = CoroutineStart.LAZY) {
            doSomething1()
            doSomething2()
            doSomething3() 
        } 
    )
}
一萌ing 2025-02-06 15:12:59

可以通过保存最后一份工作并等待完成新的Coroutine来解决:

private var lastJob: Job? = null

fun doSomething() {
    val prevJob = lastJob
    lastJob = lifecycleScope.launch {
        prevJob?.join()
        doSomething1()
        doSomething2()
        doSomething3()
    }
}

Can be solved by saving the last job and waiting for it to finish before executing a new coroutine:

private var lastJob: Job? = null

fun doSomething() {
    val prevJob = lastJob
    lastJob = lifecycleScope.launch {
        prevJob?.join()
        doSomething1()
        doSomething2()
        doSomething3()
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文