Здравствуйте, я думаю, что нашел решение этой проблемы, и я действительно хочу поделиться этим с вами, ребята.
Прежде чем начать с реального ответа Я хочу убедиться, что все понимают, как на самом деле работает Application.OnTime . И если вы уже знаете, то можете смело переходить к РЕШЕНИЕ ниже.
Давайте создадим пример TOY EXAMLPE с двумя подпрограммами Sub First () и Sub Second () и одной переменной x, которая объявлена снаружи, так что она имеет область видимости внутри всего модуля
Dim x as integer
Sub First()
x = 3
Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
x = 2*x
End Sub
Sub Second()
x = x + 1
End Sub
Я думал, что команды были выполнены в следующем порядке:
- х = 3
- Application.OnTime Now + TimeSerial (0, 0, 2), "Sub2"
- Затем через 2 секунды ожидания в Sub Second () x = x + 1, следовательно, 4
- Наконец, мы возвращаемся к Sub First (), где x = 2 * x, так что в конце x равен 8.
Оказывается, это не тот способ, которым работает VBA; вместо этого происходит:
- x = 3
- Application.OnTime Now + TimeSerial (0, 0, 2), "Sub2"
- Здесь оставшийся код в Sub First () выполняется до КОНЦА, до переключения в Sub Second ().
- Так что x = 2 * x выполняется сразу же вместе с каждой строкой кода, которая появляется до конца Sub First (). Теперь х равен 6.
- Наконец, после 2 секунд ожидания он выполняет инструкцию в Sub Second (), x = x + 1, так что в конце x равен 7
Это происходит независимо от того, сколько времени вы заставляете приложение ждать. Например, если в моем примере после
Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
VBA потребовалось 10 секунд, чтобы выполнить строку
x = 2*x
все равно придется завершить выполнение этой команды перед переключением на Sub Second ().
ПОЧЕМУ ЭТО ВАЖНО?
Потому что в свете того, что я только что объяснил, теперь я могу показать вам свое решение вопроса ОП. Тогда вы можете адаптировать его к вашим потребностям.
И ДА !!! Это также работает с For Loops!
РЕШЕНИЕ
У меня есть две подпрограммы:
BLPDownload (), где я обновляю рабочую книгу, и мне нужно ждать загрузки значений для выполнения какого-либо другого кода ...
BLPCheckForRefresh (), где я проверяю, все ли данные были загружены
Так же, как и раньше, я объявляю две переменные с областью действия уровня модуля
Dim firstRefreshDone As Boolean, Refreshing As Boolean
Sub BLPDownload()
CHECK:
Что я делаю ниже:
- проверьте, не сказал ли я уже VBA Обновить книгу. Конечно, при первом запуске макроса у вас нет; следовательно firstRefreshDone = False и он входит в этот блок оператора if.
- Затем он вызывает другую Sub BLPCheckForRefresh () и выходит из текущей подпрограммы.
И это хитрость. Для выхода из подпрограммы после вызова Application.OnTime *
Внутри BLPCheckForRefresh () происходит
- что я установил значение firstRefreshDone = True
- проверьте, есть ли в UsedRange ячейки с # N / A запрашивающими данными. Если у меня есть, значение Refreshing = True.
наконец я перезваниваю Sub BLPDownload ()
If Not firstRefreshDone Then
Application.Run "RefreshEntireWorkbook"
Application.Run "RefreshAllStaticData"
Application.OnTime Now + TimeSerial(0, 0, 4), "BLPCheckForRefresh"
Exit Sub
На этот раз firstRefreshDone = True, поэтому, если обновление также завершено, оно переходит к AFTER_REFRESH, где вы можете поместить весь нужный код, иначе ...
ElseIf Not Refreshing Then
GoTo AFTER_REFRESH
если обновление не закончено, то есть, если у меня есть ячейки с # N / A, запрашивающие данные, он вызывает другую Sub BLPCheckForRefresh () и снова выходит из текущей подпрограммы.
Эта забавная игра продолжается до тех пор, пока у нас больше не будет # N / A Запрос данных в нашем UsedRange
Else
Refreshing = False
Application.OnTime Now + TimeSerial(0, 0, 4), "BLPCheckForRefresh"
Exit Sub
End If
AFTER:
some code ...
End Sub
Это подпункт, в котором я проверяю, выполнено ли обновление.
Sub BLPCheckForRefresh()
Dim rng As Range, cl As Range
Set rng = Foglio1.UsedRange
Как объяснялось выше, я установил значение firstRefreshDone = True
firstRefreshDone = True
И это цикл, в котором я прохожу каждую ячейку в используемом диапазоне в поисках # N / A Запроса данных
On Error Resume Next
For Each cl In rng
If InStr(cl.Value2, "#N/A Request") > 0 Then
Refreshing = True
Exit For
End If
Next cl
On Error GoTo 0
Наконец, я перезваниваю Sub BLPDownload ()
Call BLPDownload
End Sub
Так что это мое решение.Я работаю для меня, и с помощью другого подвоха, который всегда использует операторы GoTo, и другой переменной Scope на уровне модуля, которая хранит счетчик числа итераций , эту структуру можно использовать и в For Loops.
При этом я хочу отметить, что, по моему мнению, наилучшее решение этой проблемы - это использование API Bloomberg, как предложил Тони Даллимор.
Надеюсь, это поможет !!