SetTimer

在指定的时间间隔内自动重复调用函数.

SetTimer Function, Period, Priority

参数

Function

类型: 函数对象

要调用的函数对象.

函数对象的引用保存在脚本的计时器列表中, 除非删除计时器, 否则不会释放该引用. 对于只运行一次的计时器, 此操作会自动发生, 但是也可以通过调用 Period(周期) 为 0 的 SetTimer 来完成.

如果 Function 省略, 如果有的话, SetTimer 将在启动当前线程的定时器上运行. 例如, SetTimer , 0 可以在一个定时器函数中用于标记要删除的定时器, 而 SetTimer , 1000 将更新当前定时器的 Period.

注意: 传递空变量或返回空值的表达式被看做一个错误. 这个参数必须为非空的值或完全忽略.

Period

类型: 整数

如果省略, 并且计时器不存在, 将创建一个周期为 250 的计时器. 如果省略, 并且计时器已经存在, 将重置之前的周期, 除非指定了 Priority. 否则, 参数的绝对值被用作在执行计时器之前必须经过的大约毫秒数. 定时器将被自动重置. 可以设置为自动重复或只运行一次:

Period 的绝对值不能大于 4294967295 ms (49.7 天).

Priority

类型: 整数

如果省略, 默认为 0. 否则, 请指定一个介于 -2147483648 和 2147483647 之间的整数(或为表达式) 来表示计时器的优先级. 有关详情, 请参阅线程.

要改变现有计时器的优先级而不以任何其他方式影响它, 请省略 Period.

备注

计时器很有用, 因为它们是异步运行的, 这意味着它们会以指定的频率(间隔) 运行, 即使脚本正等待窗口, 显示对话框或忙于其他任务的时候. 它们的许多应用实例包括了当用户空闲时(如 A_TimeIdle 反映的那样) 执行一些动作或在不需要的窗口出现时关闭它们.

尽管计时器给人一种脚本同时执行多个任务的错觉, 实际并非如此. 相反地, 定时子程序只是被当作其他线程来处理: 它们可以中断另一个线程或被另一个线程中断, 例如 热键子程序. 有关详情, 请参阅线程.

每当创建或重新启用计时器或用新的 period 更新计时器时, 它的函数不会立即被调用; 必须先经过它的 period 时间. 如果您希望立即开始计时器的首次执行, 请直接调用计时器的函数(但是, 这不会像定时器本身那样启动一个新的线程; 所以像 SendMode 这样的设置不会以其默认值启动).

重置: 如果 SetTimer 被用在一个已经存在的计时器上, 那么这个计时器会被重置(除非指定了 Priority 并且省略了 Period); 换句话说, 在经过它的整个周期后函数才会再次被调用.

计时器精度: 由于操作系统中计时系统的精确度, Period 通常会被向上取整到最近的 10 或 15.6 毫秒的倍数(取决于安装的硬件和驱动程序的类型). 可以通过 Loop+Sleep 实现更短的延迟, 如 DllCall+timeBeginPeriod+Sleep 所示.

可靠性: 在以下情况下, 计时器可能无法按预期时间运行:

  1. 其他应用程序对 CPU 造成了很大的负荷.
  2. 计时器函数在计时器周期到期时仍在运行.
  3. 有太多其他竞争的计时器.
  4. 计时器被另一个线程中断, 即另一个定时函数, 热键子程序自定义菜单项(这可以通过 Critical 来避免). 如果发生这种情况, 而中断的线程需要很长时间才能完成, 那么被中断的计时器将在这段时间内有效地被禁用. 但是, 任何其他的计时器都会通过中断中断第一个计时器的线程继续运行.
  5. 使用 CriticalThread Interrupt/Priority 后脚本会变成不可中断的. 在这样的时期, 计时器不会运行. 之后, 当脚本恢复可中断时, 任何过时的计时器会尽快运行一次然后恢复到正常的调度.

虽然当脚本被挂起时, 计时器会运行, 但如果当前线程Thread NoTimers 正在生效或任何线程暂停时, 计时器都不会运行. 此外, 当用户正在浏览脚本的某个菜单(如托盘图标菜单或菜单栏) 时, 它们也不会运行.

因为定时器是通过临时中断脚本的当前线程来运行的, 所以当长时间的中断是不可取的, 它们的函数应该保持短暂的(以便快速完成).

其他备注: 临时的计时器经常可以在它自己的函数中禁用(请参阅此页面底部的示例).

每当函数被定时器调用时, 它都会以默认的设置值(如 SendMode) 重新开始. 这些默认值可以在脚本启动中更改.

如果热键的响应时间非常重要(如在游戏中) 并且脚本中包含的任何计时器, 其函数的执行时间超过 5 毫秒, 请使用下面的函数来避免 15 毫秒的延迟. 否则, 如果在计时器线程处于不中断期的时候按下热键, 就会出现这样的延迟:

Thread "Interrupt", 0  ; 使所有线程始终处于可中断状态.

如果一个计时器的函数正在运行时被禁用, 那么该函数将继续运行, 直到完成.

KeyHistory 功能显示存在的计时器数目, 以及当前启用的数目.

线程, Thread (函数), Critical, 函数对象

示例

当不想要的窗口出现时关闭它们.

SetTimer CloseMailWarnings, 250

CloseMailWarnings()
{
    WinClose "Microsoft Outlook", "A timeout occured while communicating"
    WinClose "Microsoft Outlook", "A connection to the server could not be established"
}

等待特定的窗口出现, 然后通知用户.

SetTimer Alert1, 500

Alert1()
{
    if !WinExist("Video Conversion", "Process Complete")
        return
    ; 否则:
    SetTimer , 0  ; 即此处计时器关闭自己.
    MsgBox "The video conversion is finished."
}

检测热键的单次, 两次和三次按下. 这样允许热键根据您按下次数的多少执行不同的操作.

#c::
KeyWinC(ThisHotkey)  ; 这是一个命名的函数热键.
{
    static winc_presses := 0
    if winc_presses > 0 ; SetTimer 已经启动, 所以我们记录键击.
    {
        winc_presses += 1
        return
    }
    ; 否则, 这是新开始系列中的首次按下. 把次数设为 1 并启动计时器
    winc_presses := 1
    SetTimer After400, -400 ; 在 400 毫秒内等待更多的键击.

    After400()  ; 这是一个嵌套函数.
    {
        if winc_presses = 1 ; 此键按下了一次.
        {
            Run "m:\"  ; 打开文件夹.
        }
        else if winc_presses = 2 ; 此键按下了两次.
        {
            Run "m:\multimedia"  ; 打开一个不同的文件夹.
        }
        else if winc_presses > 2
        {
            MsgBox "Three or more clicks detected."
        }
        ; 不论触发了上面的哪个动作, 都对 count 进行重置
        ; 为下一个系列的按键做准备:
        winc_presses := 0
    }
}

使用方法作为计时器函数.

counter := SecondCounter()
counter.Start
Sleep 5000
counter.Stop
Sleep 2000

; 一个记录秒数的示例类...
class SecondCounter {
    __New() {
        this.interval := 1000
        this.count := 0
        ; Tick() 有一个隐式参数 "this", 其引用一个对象
        ; 所以, 我们需要创建一个封装了 "this " 和调用方法的函数:
        this.timer := ObjBindMethod(this, "Tick")
    }
    Start() {
        SetTimer this.timer, this.interval
        ToolTip "Counter started"
    }
    Stop() {
        ; 要关闭计时器, 我们必须传递和之前一样的对象:
        SetTimer this.timer, 0
        ToolTip "Counter stopped at " this.count
    }
    ; 本例中, 计时器调用了以下方法:
    Tick() {
        ToolTip ++this.count
    }
}

示例 #4 的备注: