-
-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add JSTimer implementation with tests (#46)
Depends on #45. This is also a prerequisite for a future `JSPromise` PR with tests. I intentionally didn't match the JS API in this PR, as a special care is needed to hold a reference to the timer closure and to call `.release()` on it. Here, a user is supposed to hold a reference to a `JSTimer` instance for it to stay valid. The API is also intentionally simple, the timer is started right away, and the only way to invalidate the timer is to bring its reference count to zero. If you see a better way to manage closures passed to `setTimeout` and `setInterval`, I'd be happy to consider that. Also, Node.js and browser APIs are slightly different. `setTimeout`/`setInterval` return an object in Node.js, while browsers return a number. Fortunately, `clearTimeout` and `clearInterval` take corresponding types as their arguments, and we can store either as `JSValue`, so we can treat both cases uniformly.
- Loading branch information
1 parent
3220b3c
commit 0ac8b37
Showing
2 changed files
with
88 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** This timer type hides [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) | ||
/ [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval) and | ||
[`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) | ||
/ [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) | ||
pairs of calls for you. It intentionally doesn't match the JavaScript API, as a special care is | ||
needed to hold a reference to the timer closure and to call `JSClosure.release()` on it when the | ||
timer is deallocated. As a user, you have to hold a reference to a `JSTimer` instance for it to stay | ||
valid. The `JSTimer` API is also intentionally trivial, the timer is started right away, and the | ||
only way to invalidate the timer is to bring the reference count of the `JSTimer` instance to zero, | ||
either by storing the timer in an optional property and assigning `nil` to it or by deallocating the | ||
object that owns it for invalidation. | ||
*/ | ||
public final class JSTimer { | ||
/// Indicates whether this timer instance calls its callback repeatedly at a given delay. | ||
public let isRepeating: Bool | ||
|
||
private let closure: JSClosure | ||
|
||
/** Node.js and browser APIs are slightly different. `setTimeout`/`setInterval` return an object | ||
in Node.js, while browsers return a number. Fortunately, clearTimeout and clearInterval take | ||
corresponding types as their arguments, and we can store either as JSValue, so we can treat both | ||
cases uniformly. | ||
*/ | ||
private let value: JSValue | ||
private let global = JSObject.global | ||
|
||
/** | ||
Creates a new timer instance that calls `setInterval` or `setTimeout` JavaScript functions for you | ||
under the hood. | ||
- Parameters: | ||
- millisecondsDelay: the amount of milliseconds before the `callback` closure is executed. | ||
- isRepeating: when `true` the `callback` closure is executed repeatedly at given | ||
`millisecondsDelay` intervals indefinitely until the timer is deallocated. | ||
- callback: the closure to be executed after a given `millisecondsDelay` interval. | ||
*/ | ||
public init(millisecondsDelay: Double, isRepeating: Bool = false, callback: @escaping () -> ()) { | ||
closure = JSClosure { _ in callback() } | ||
self.isRepeating = isRepeating | ||
if isRepeating { | ||
value = global.setInterval.function!(closure, millisecondsDelay) | ||
} else { | ||
value = global.setTimeout.function!(closure, millisecondsDelay) | ||
} | ||
} | ||
|
||
/** Makes a corresponding `clearTimeout` or `clearInterval` call, depending on whether this timer | ||
instance is repeating. The `closure` instance is released manually here, as it is required for | ||
bridged closure instances. | ||
*/ | ||
deinit { | ||
if isRepeating { | ||
global.clearInterval.function!(value) | ||
} else { | ||
global.clearTimeout.function!(value) | ||
} | ||
closure.release() | ||
} | ||
} |