我正在使用ComboBox和VBA在Excel内部进行下拉。我能够让它向远程api发出请求,将返回的数据添加到隐藏的工作表中,并根据api的结果更新下拉选项。
我要做的是限制api请求。目前,如果已经处理了api请求,Excel似乎不会触发sub。这并不理想,因为人们通常会快速连续输入多个角色。我想为每个子调用添加一个计时器,如果在〜250ms内没有对子函数的新调用,则发送api请求。如果在250ms期间进行了另一次调用,我想取消该子程序的执行。
最初我尝试创建一个全局“process_id”,其中sub将为当前全局添加1,将其本地id设置为该值,等待x时间,检查其本地ID ===全局ID,以及是否不要退出子。然而现在似乎第二个子节点在计时器等待x时间时从不运行,因此第一个子节点仍然运行,仅在x秒后运行(并且第二个子节点根本不运行)。
如何在Excel VBA中限制子函数,以便在第一个子函数等待时运行相同的子函数?
答案 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
变量。