• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import org.w3c.dom.Window
8 
9 /**
10  * Converts an instance of [Window] to an implementation of [CoroutineDispatcher].
11  */
12 public fun Window.asCoroutineDispatcher(): CoroutineDispatcher =
13     @Suppress("UnsafeCastFromDynamic")
14     asDynamic().coroutineDispatcher ?: WindowDispatcher(this).also {
15         asDynamic().coroutineDispatcher = it
16     }
17 
18 /**
19  * Suspends coroutine until next JS animation frame and returns frame time on resumption.
20  * The time is consistent with [window.performance.now()][org.w3c.performance.Performance.now].
21  * This function is cancellable. If the [Job] of the current coroutine is completed while this suspending
22  * function is waiting, this function immediately resumes with [CancellationException].
23  */
contnull24 public suspend fun Window.awaitAnimationFrame(): Double = suspendCancellableCoroutine { cont ->
25     asWindowAnimationQueue().enqueue(cont)
26 }
27 
Windownull28 private fun Window.asWindowAnimationQueue(): WindowAnimationQueue =
29     @Suppress("UnsafeCastFromDynamic")
30     asDynamic().coroutineAnimationQueue ?: WindowAnimationQueue(this).also {
31         asDynamic().coroutineAnimationQueue = it
32     }
33 
34 private class WindowAnimationQueue(private val window: Window) {
35     private val dispatcher = window.asCoroutineDispatcher()
36     private var scheduled = false
37     private var current = ArrayDeque<CancellableContinuation<Double>>()
38     private var next = ArrayDeque<CancellableContinuation<Double>>()
39     private var timestamp = 0.0
40 
enqueuenull41     fun enqueue(cont: CancellableContinuation<Double>) {
42         next.addLast(cont)
43         if (!scheduled) {
44             scheduled = true
45             window.requestAnimationFrame { ts ->
46                 timestamp = ts
47                 val prev = current
48                 current = next
49                 next = prev
50                 scheduled = false
51                 process()
52             }
53         }
54     }
55 
processnull56     fun process() {
57         while(true) {
58             val element = current.removeFirstOrNull() ?: return
59             with(element) { dispatcher.resumeUndispatched(timestamp) }
60         }
61     }
62 }
63