如何在VBA中限制功能?

时间:2018-04-28 04:45:20

标签: excel vba excel-vba

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

我要做的是限制api请求。目前,如果已经处理了api请求,Excel似乎不会触发sub。这并不理想,因为人们通常会快速连续输入多个角色。我想为每个子调用添加一个计时器,如果在〜250ms内没有对子函数的新调用,则发送api请求。如果在250ms期间进行了另一次调用,我想取消该子程序的执行。

最初我尝试创建一个全局“process_id”,其中sub将为当前全局添加1,将其本地id设置为该值,等待x时间,检查其本地ID ===全局ID,以及是否不要退出子。然而现在似乎第二个子节点在计时器等待x时间时从不运行,因此第一个子节点仍然运行,仅在x秒后运行(并且第二个子节点根本不运行)。

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

3 个答案:

答案 0 :(得分:1)

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

希望以下内容适合您:

  

当设置定时器时,不应再设置定时器,当定时器触发事件​​时,定时器会自动停止,因此在“延迟时间秒”期间会有多个呼叫。句点只应该导致对API的一次调用

' 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

答案 1 :(得分:1)

认为你真正想要的是"在我停止输入"之前不要调用API。 (即在前一个按钮的x毫秒内没有再发生按键时)。

如果这就是你想要的东西,那么这个(给@Tragamor的回答)应该这样做。这与您在js中使用window.setTimeout所做的更接近。

在常规代码模块中:

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

答案 2 :(得分:0)

坚持使用全局变量的方法,让一个人跟踪Timer函数。不幸的是你没有提供你的代码,所以我只能假设你应该如何实现它。

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

如果您不想退出sub,请将API调用包装在If语句中。

Private myTimer As Single

Sub foo()

    ' Non-API related code

    If Timer - myTimer > 0.25 Then
        callMyAPI
        myTimer = Timer
    End If

    'Non-API related code

End Sub

每次API调用都会重置myTimer变量。