Let's say that you have a function that interfaces with an external device. Every time you call the function there is a noticeable lag. If you check the value, it lags. If you set the value, it lags.
Assume that you'd like to change this value by adding +1 or subtracting -1. Since the function is so slow, if you press [+1] 3 times, it takes one second for the value to change.
Let's make this harder. You want this slow, blocking function to be controlled by the mouse wheel. Every time you scroll the mouse wheel, millions of events are generated. But since your function is so slow, it takes 1000 seconds for all events to be processed, and in the meantime if you scroll the mouse wheel, more events are generated, creating a very long queue of events waiting to be executed.
Q: How do you filter out the excess events in AutoHotkey v2?
A: Use a function of the following form: SetTimer fn.bind(value, &value), -1.
Here SetTimer fn, -1 means execute the func as soon as you can, asynchronously, by placing the function call in the queue.
fn.bind(value, &value)
This is the interesting part. The value is turned into a number, and the VarRef object &value is a reference to the value.
Example: The user scrolls their mouse generating 3 scrolling events, with each event incrementing by 1. The function fn receives:
Code: Select all
1, &value
2, &value
3, &value
Code: Select all
fn(value, valueRef := "") {
; Retrieve the latest value.
current := %valueRef%
; Check if the latest value equals the value passed originally.
if current == value {
; Mimic a blocking call
Critical 'On'
Sleep(250) ; Simulate a blocking call that needs 250 milliseconds to return
global slow := current ; Set the slow value to the latest value.
Critical 'Off'
}
}
And the current value is checked against the originally passed value to determine whether the event should be discarded.
So now it becomes:
Code: Select all
1, 1 ; executes slow function because the current value is equal to the passed value (which is one above zero)
2, 3 ; not executed, as the slow function is blocking...
3, 3 ; executes slow function because the newly set current value is equal to the passed value (because the current value has been set to 3)
When value == %valueRef%, this describes exactly when the mouse wheel stops scrolling. If you scrolled from 1—50, then the last value will be 50, therefore the 50th function call is executed, 1—49 fail the equality and are discarded.
If you scroll really slow, every line synchronizes.
Here's a script for you guys to play with:
Code: Select all
#Requires AutoHotkey v2.0-beta
global fast := 0 ; Non-blocking. Instant. Is a local copy of the slow value.
; The purpose of this script is to examine the best way to deal with a blocking call that takes 250 ms to return.
; When the wheel is scrolled, a million events are queued. How do we deal with this such that
WheelUp:: {
global fast := fast + 1
SetTimer fn.bind(fast, &fast), -1 ; Note that the current value is passed, as well as a reference to the current value
}
WheelDown:: {
global fast := fast - 1
SetTimer fn.bind(fast, &fast), -1 ; Note that the current value is passed, as well as a reference to the current value
}
fn(value, valueRef := "") {
static log := ""
; Retrieve the latest value.
current := %valueRef%
; Check if the latest value equals the value passed originally.
if current == value {
; Mimic a blocking call
Critical 'On'
Sleep(250) ; Simulate a blocking call that needs 250 milliseconds to return
Critical 'Off'
log .= value ", " current " synchronize `t" current "`n"
}
else {
log .= value ", " current "`n"
}
Tooltip log
}
MButton:: Reload
Esc:: ExitApp
Getting and setting the monitor brightness is very hard. The programatic way is to do it yourself!!!