xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-core/js/src/Window.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)

<lambda>null1 package kotlinx.coroutines
2 
3 import org.w3c.dom.Window
4 
5 /**
6  * Converts an instance of [Window] to an implementation of [CoroutineDispatcher].
7  */
8 public fun Window.asCoroutineDispatcher(): CoroutineDispatcher =
9     @Suppress("UnsafeCastFromDynamic")
10     asDynamic().coroutineDispatcher ?: WindowDispatcher(this).also {
11         asDynamic().coroutineDispatcher = it
12     }
13 
14 /**
15  * Suspends coroutine until next JS animation frame and returns frame time on resumption.
16  * The time is consistent with [window.performance.now()][org.w3c.performance.Performance.now].
17  * This function is cancellable. If the [Job] of the current coroutine is completed while this suspending
18  * function is waiting, this function immediately resumes with [CancellationException].
19  */
contnull20 public suspend fun Window.awaitAnimationFrame(): Double = suspendCancellableCoroutine { cont ->
21     asWindowAnimationQueue().enqueue(cont)
22 }
23 
Windownull24 private fun Window.asWindowAnimationQueue(): WindowAnimationQueue =
25     @Suppress("UnsafeCastFromDynamic")
26     asDynamic().coroutineAnimationQueue ?: WindowAnimationQueue(this).also {
27         asDynamic().coroutineAnimationQueue = it
28     }
29 
30 private class WindowAnimationQueue(private val window: Window) {
31     private val dispatcher = window.asCoroutineDispatcher()
32     private var scheduled = false
33     private var current = ArrayDeque<CancellableContinuation<Double>>()
34     private var next = ArrayDeque<CancellableContinuation<Double>>()
35     private var timestamp = 0.0
36 
enqueuenull37     fun enqueue(cont: CancellableContinuation<Double>) {
38         next.addLast(cont)
39         if (!scheduled) {
40             scheduled = true
41             window.requestAnimationFrame { ts ->
42                 timestamp = ts
43                 val prev = current
44                 current = next
45                 next = prev
46                 scheduled = false
47                 process()
48             }
49         }
50     }
51 
processnull52     fun process() {
53         while(true) {
54             val element = current.removeFirstOrNull() ?: return
55             with(element) { dispatcher.resumeUndispatched(timestamp) }
56         }
57     }
58 }
59