如何在VBA中调节功能?

如何在VBA中调节功能?,vba,excel,Vba,Excel,我正在使用组合框和VBA在Excel内部进行下拉。我已经能够让它向远程api发出请求,将返回的数据添加到隐藏的工作表中,并根据api的结果更新下拉选项 我要做的是限制api请求。目前,如果Excel已经在处理api请求,它似乎不会启动sub。这并不理想,因为人们通常会连续快速键入多个字符。我想为每个子调用添加一个计时器,如果在~250ms内没有新的子函数调用,则发送api请求。如果在250ms内再次调用,我想取消该sub的执行 最初,我尝试创建一个全局“process_id”,子进程将向当前全局

我正在使用组合框和VBA在Excel内部进行下拉。我已经能够让它向远程api发出请求,将返回的数据添加到隐藏的工作表中,并根据api的结果更新下拉选项

我要做的是限制api请求。目前,如果Excel已经在处理api请求,它似乎不会启动sub。这并不理想,因为人们通常会连续快速键入多个字符。我想为每个子调用添加一个计时器,如果在~250ms内没有新的子函数调用,则发送api请求。如果在250ms内再次调用,我想取消该sub的执行

最初,我尝试创建一个全局“process_id”,子进程将向当前全局添加1,将其本地id设置为该值,等待x时间,检查其本地id==全局id,如果不退出子进程,则退出子进程。但是,现在似乎第二个子进程在计时器等待x时间时从不运行,因此第一个子进程仍然运行,仅仅过了x秒(第二个潜艇根本就没有运行过)


如何在Excel VBA中限制子函数,以便在第一个子函数等待时可以运行相同的子函数?

按照您使用全局变量的方法,保留一个来跟踪
计时器
函数。不幸的是,您没有提供代码,所以我只能假设您应该如何实现它

Private myTimer As Single   '// Global Variable, outside of any routine

Sub foo()               

    If Timer - myTimer < 0.25 Then Exit Sub

    callMyAPI
    myTimer = Timer

End Sub

在每次API调用中,您将重置
myTimer
变量。

如前所述;您需要一个毫秒精度的异步计时器

希望以下内容对您有用:

设置计时器时,不应再设置其他计时器,并且当计时器触发事件时,计时器会自行停止,因此在“DelayTimeSeconds”期间的多个调用只会导致对API的一个调用

我认为您真正想要的是“在我停止键入之前不要调用API”(即,在上一次键入的x毫秒内没有进一步的按键)

如果这就是你想要的,那么这个(帽子尖指向@Tragamor的答案)应该可以做到。这与使用
window.setTimeout
在js中所做的工作非常接近

在常规代码模块中:

Option Explicit

Public Declare Function SetTimer Lib "user32" ( _
    ByVal HWnd As Long, ByVal nIDEvent As Long, _
    ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib "user32" ( _
    ByVal HWnd As Long, ByVal nIDEvent As Long) As Long

Private TimerID As Long

'this function called from the control's Change event
Function CallApi()
    Const DelayMsec As Long = 500
    If TimerID > 0 Then KillTimer 0&, TimerID 'kill any existing timer
    TimerID = SetTimer(0&, 0&, DelayMsec, AddressOf CallApi2) 'set a timer
End Function

'this function called from the timer
Sub CallApi2()
    If TimerID > 0 Then KillTimer 0&, TimerID
    Debug.Print "Calling API with '" & Sheet1.TextBox1.Text & "'"
End Sub

我不确定您是否可以在VBA中设置异步计时器,这正是您在这里需要的。VBA中通常使用的计时器或多或少都是阻塞的,除了OnTime,但它的最小分辨率为1秒…PS:显然,世界上最快的键入wpm大约为215,因此大约0.4s的延迟时间可能是一个可接受的值
' Adapted from https://groups.google.com/forum/?hl=en#!topic/microsoft.public.excel.programming/Ce--7sympZM
' These procedures require that you are using Office 2000 or later, because of the AddressOf function

Public Declare Function SetTimer Lib "user32" ( _
    ByVal HWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib "user32" ( _
    ByVal HWnd As Long, ByVal nIDEvent As Long) As Long

Private AllowFireTime As Single
Private TimerID As Long
Private TimerSeconds As Single
Private TimerSet As Boolean

Const DelayTimeSeconds As Single = 0.75

Sub TestFireDelay()
    CallAPIProc
End Sub

Private Function CallAPIProc()
    Dim NowTime As Single: NowTime = Timer
    If AllowFireTime > NowTime Then
        If TimerSet = False Then StartTimer AllowFireTime - NowTime
    Else
        AllowFireTime = NowTime + DelayTimeSeconds

        Call TestCall
        ' Code for API Call

    End If
End Function

Function StartTimer(Optional TimerSeconds = 1) ' how often to "pop" the timer.
    TimerID = SetTimer(0&, 0&, TimerSeconds * 1000&, AddressOf TimerProc)
    TimerSet = True
End Function

Function EndTimer()
    On Error Resume Next
    KillTimer 0&, TimerID
    TimerSet = False
End Function

Function TimerProc(ByVal HWnd As Long, ByVal uMsg As Long, ByVal nIDEvent As Long, ByVal dwTimer As Long)
    ' The procedure is called by Windows. Put your timer-related code here.
    '
    Call EndTimer
    Call CallAPIProc
End Function

Function TestCall()
    Debug.Print Format(Now, "hh:mm:ss") & "." & Strings.Right(Strings.Format(Timer, "#0.00"), 2)
End Function
Option Explicit

Public Declare Function SetTimer Lib "user32" ( _
    ByVal HWnd As Long, ByVal nIDEvent As Long, _
    ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib "user32" ( _
    ByVal HWnd As Long, ByVal nIDEvent As Long) As Long

Private TimerID As Long

'this function called from the control's Change event
Function CallApi()
    Const DelayMsec As Long = 500
    If TimerID > 0 Then KillTimer 0&, TimerID 'kill any existing timer
    TimerID = SetTimer(0&, 0&, DelayMsec, AddressOf CallApi2) 'set a timer
End Function

'this function called from the timer
Sub CallApi2()
    If TimerID > 0 Then KillTimer 0&, TimerID
    Debug.Print "Calling API with '" & Sheet1.TextBox1.Text & "'"
End Sub