|
| 1 | +/** This timer type hides [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) |
| 2 | +/ [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval) and |
| 3 | +[`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) |
| 4 | +/ [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) |
| 5 | +pairs of calls for you. It intentionally doesn't match the JavaScript API, as a special care is |
| 6 | +needed to hold a reference to the timer closure and to call `JSClosure.release()` on it when the |
| 7 | +timer is deallocated. As a user, you have to hold a reference to a `JSTimer` instance for it to stay |
| 8 | +valid. The `JSTimer` API is also intentionally trivial, the timer is started right away, and the |
| 9 | +only way to invalidate the timer is to bring the reference count of the `JSTimer` instance to zero, |
| 10 | +either by storing the timer in an optional property and assigning `nil` to it or by deallocating the |
| 11 | +object that owns it for invalidation. |
| 12 | +*/ |
| 13 | +public final class JSTimer { |
| 14 | + /// Indicates whether this timer instance calls its callback repeatedly at a given delay. |
| 15 | + public let isRepeating: Bool |
| 16 | + |
| 17 | + private let closure: JSClosure |
| 18 | + |
| 19 | + /** Node.js and browser APIs are slightly different. `setTimeout`/`setInterval` return an object |
| 20 | + in Node.js, while browsers return a number. Fortunately, clearTimeout and clearInterval take |
| 21 | + corresponding types as their arguments, and we can store either as JSValue, so we can treat both |
| 22 | + cases uniformly. |
| 23 | + */ |
| 24 | + private let value: JSValue |
| 25 | + private let global = JSObject.global |
| 26 | + |
| 27 | + /** |
| 28 | + Creates a new timer instance that calls `setInterval` or `setTimeout` JavaScript functions for you |
| 29 | + under the hood. |
| 30 | + - Parameters: |
| 31 | + - millisecondsDelay: the amount of milliseconds before the `callback` closure is executed. |
| 32 | + - isRepeating: when `true` the `callback` closure is executed repeatedly at given |
| 33 | + `millisecondsDelay` intervals indefinitely until the timer is deallocated. |
| 34 | + - callback: the closure to be executed after a given `millisecondsDelay` interval. |
| 35 | + */ |
| 36 | + public init(millisecondsDelay: Double, isRepeating: Bool = false, callback: @escaping () -> ()) { |
| 37 | + closure = JSClosure { _ in callback() } |
| 38 | + self.isRepeating = isRepeating |
| 39 | + if isRepeating { |
| 40 | + value = global.setInterval.function!(closure, millisecondsDelay) |
| 41 | + } else { |
| 42 | + value = global.setTimeout.function!(closure, millisecondsDelay) |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + /** Makes a corresponding `clearTimeout` or `clearInterval` call, depending on whether this timer |
| 47 | + instance is repeating. The `closure` instance is released manually here, as it is required for |
| 48 | + bridged closure instances. |
| 49 | + */ |
| 50 | + deinit { |
| 51 | + if isRepeating { |
| 52 | + global.clearInterval.function!(value) |
| 53 | + } else { |
| 54 | + global.clearTimeout.function!(value) |
| 55 | + } |
| 56 | + closure.release() |
| 57 | + } |
| 58 | +} |
0 commit comments