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