TL;DR —
Application.OnTimeno pone en marcha un temporizador. Hace una reserva en la cola de Excel: a esta hora, ejecuta esta macro. Tu código termina al instante y Excel sigue utilizable; cuando llega la hora y Excel está listo, ejecuta la macro. La reserva se identifica por la hora exacta y el nombre de la macro, así que para cancelarla tienes que volver a pasar las dos cosas, lo que obliga a guardar la hora en una variable de nivel de módulo. Para una tarea repetida, la propia macro reserva su siguiente ejecución. Cancela enWorkbook_BeforeClose, porque cerrar el libro no cancela nada: Excel volverá a abrir el archivo para cumplir la cita.
Private mNextRun As Date ' la reserva que quizas tengas que cancelar
Sub StartRefresh()
mNextRun = Now + TimeSerial(0, 5, 0) ' dentro de cinco minutos
Application.OnTime mNextRun, "RefreshPrices"
End Sub
Sub StopRefresh()
On Error Resume Next ' error 1004 si no hay nada reservado
Application.OnTime mNextRun, "RefreshPrices", Schedule:=False
On Error GoTo 0
End Sub
Este es el segundo artículo de una serie sobre macros que Excel ejecuta por ti. La
guía de atajos de teclado trata las macros que se lanzan desde el teclado, y la
guía de SendKeys las pulsaciones simuladas. La idea que las une: ninguna llama a
tu macro directamente. Cada una deja una petición en la cola de Excel, y Excel la ejecuta cuando está libre y
listo. Con OnTime, la petición lleva una hora pegada, y se queda en la cola hasta que se ejecuta o hasta
que la retiras con exactamente los mismos datos.
Lo que aprenderás
- El modelo mental: una reserva, no un temporizador en marcha
- Ejecutar una macro a una hora del día, y cada N minutos
- Por qué falla la cancelación, y la variable que lo arregla
- El temporizador que ya no puedes parar tras un restablecimiento del código
- Por qué cerrar el libro hace que Excel lo vuelva a abrir
- Excel ocupado,
LatestTimey una programación que se desvía - Cuándo
OnTimees la herramienta equivocada
El modelo mental: una reserva, no un temporizador
Compara OnTime con un bucle que espera:
' Bloquea Excel: no puede pasar nada mas durante cinco minutos
Application.Wait Now + TimeSerial(0, 5, 0)
RefreshPrices
' Hace una reserva y vuelve al instante: Excel sigue utilizable
Application.OnTime Now + TimeSerial(0, 5, 0), "RefreshPrices"
Application.Wait y Sleep mantienen tu macro en marcha y Excel
congelado. OnTime le pasa una nota a Excel y termina. Entre ahora y la reserva no se está ejecutando ningún
código VBA; el usuario trabaja con normalidad.
Excel lleva una lista de estas reservas. Cada entrada tiene dos claves, una fecha y hora y un nombre de procedimiento, y Excel encuentra una entrada haciendo coincidir las dos. Todo lo demás de este artículo sale de ahí: puedes reservar varias, cancelas una nombrándola con exactitud, y la lista pertenece a Excel, no a tu libro.
Ejecutar una macro a una hora del día
Dale a OnTime una fecha y una hora completas, para que no haya duda sobre qué día quieres decir:
Sub BookDailyExport()
Dim runAt As Date
runAt = Date + TimeSerial(17, 0, 0) ' hoy a las 17:00
If runAt <= Now Then runAt = runAt + 1 ' ya ha pasado: manana a las 17:00
Application.OnTime runAt, "DailyExport"
End Sub
Muchos ejemplos pasan solo TimeValue("17:00:00"). Construir tú la fecha deja la regla a la vista en el
código: si la hora ya ha pasado, la próxima ejecución es mañana, no ahora. La guía de Now
explica por qué Date más una hora da un valor real de fecha y hora, y TimeSerial mantiene los cálculos a
salvo de problemas de configuración regional.
Ejecutar una macro cada N minutos
No hay opción de repetición. Una tarea repetida es una macro que reserva su propia siguiente ejecución al final:
Private mNextRun As Date
Private Const INTERVAL_MIN As Long = 5
Sub StartRefresh()
mNextRun = Now + TimeSerial(0, INTERVAL_MIN, 0)
Application.OnTime mNextRun, "RefreshPrices"
End Sub
Sub RefreshPrices()
ThisWorkbook.Worksheets("Prices").Range("A1").CurrentRegion.Calculate
' ... el trabajo de verdad ...
StartRefresh ' reserva la siguiente ejecucion
End Sub
StartRefresh arranca la cadena y RefreshPrices la mantiene viva. Para detener la cadena tienes que
eliminar la única reserva que está hecha en ese momento, y ahí es donde falla casi todo el código con
OnTime.
Cancelar: la hora exacta, o el error 1004
Para cancelar, vuelve a llamar a OnTime con Schedule:=False. Excel busca entonces una entrada con la
misma hora y el mismo nombre de procedimiento. Esto parece correcto y no funciona:
' Mal: Now ya ha avanzado, asi que esta hora no coincide con ninguna reserva
Application.OnTime Now + TimeSerial(0, 5, 0), "RefreshPrices", Schedule:=False
No hay ninguna reserva a esa hora nueva, así que Excel lanza el Run-time error 1004: Method 'OnTime' of
object '_Application' failed (en español: Error 1004 en tiempo de ejecución: Error en el método 'OnTime' de
objeto '_Application'), y la reserva real sigue en pie. Por eso la hora vive en una variable de nivel de
módulo (mNextRun arriba). Reserva con ella, cancela con ella, y los datos siempre coinciden.
El On Error Resume Next de StopRefresh es deliberado y cubre una sola línea: si no hay nada reservado,
por ejemplo porque la macro ya se ejecutó, la cancelación lanza el 1004 y no hay nada que hacer al respecto.
La guía de On Error explica por qué conviene que un controlador así sea tan
estrecho.
El temporizador que ya no puedes parar
La variable tiene un punto débil. Las variables de nivel de módulo se borran cada vez que el proyecto de VBA
se restablece: cuando pulsas el botón Restablecer, cuando un error no controlado termina el código, cuando el
código ejecuta End o cuando editas código mientras está en pausa. La
guía de variables globales los enumera todos.
Tras un restablecimiento, mNextRun vale 0. La reserva sigue en la lista de Excel, pero tu código ya no
conoce su hora, así que StopRefresh no cancela nada. La macro sigue ejecutándose cada cinco minutos y se
vuelve a reservar cada vez.
Guarda una copia de la hora en algún sitio que sobreviva a un restablecimiento, como una celda de una hoja de configuración oculta:
Sub StartRefresh()
mNextRun = Now + TimeSerial(0, INTERVAL_MIN, 0)
ThisWorkbook.Worksheets("Settings").Range("B2").Value = mNextRun
Application.OnTime mNextRun, "RefreshPrices"
End Sub
Sub StopRefresh()
If mNextRun = 0 Then mNextRun = ThisWorkbook.Worksheets("Settings").Range("B2").Value
On Error Resume Next
Application.OnTime mNextRun, "RefreshPrices", Schedule:=False
On Error GoTo 0
mNextRun = 0
End Sub
Si aun así se te escapa un temporizador desbocado mientras desarrollas, cerrar Excel por completo vacía la lista.
Cerrar el libro no cancela nada
La reserva pertenece a Excel, no al libro. Si el usuario cierra tu libro y Excel sigue abierto, a la hora reservada Excel vuelve a abrir el libro para ejecutar la macro. Para el usuario, un archivo que había cerrado reaparece solo, y si la macro reserva la siguiente ejecución, sigue pasando.
Así que todo libro que reserva una ejecución debe cancelarla al cerrarse:
' En ThisWorkbook
Private Sub Workbook_BeforeClose(Cancel As Boolean)
StopRefresh
End Sub
Si después el usuario cancela el cierre en el aviso de guardar, el libro sigue abierto con el temporizador
parado. Si eso importa, vuelve a arrancarlo en Workbook_Activate. La
guía de BeforeClose trata ese caso.
Excel ocupado, LatestTime y una programación que se desvía
Aquí se aplica la regla de la serie: Excel solo ejecuta la reserva cuando está listo. Si a las 17:00 el usuario está escribiendo en una celda, tiene un cuadro de diálogo abierto o se está ejecutando otra macro, la macro espera hasta que eso termine.
El tercer argumento, LatestTime, pone un límite a esa espera. Si Excel no puede ejecutar la macro antes de
esa hora, la reserva se descarta:
' Ejecutar a las 17:00, o no ejecutar si Excel sigue ocupado a las 17:05
Application.OnTime runAt, "DailyExport", runAt + TimeSerial(0, 5, 0)
La espera también explica por qué se desvía una tarea repetida. Now + 5 minutos, medido al final de cada
ejecución, suma a cada intervalo lo que dura la ejecución y cualquier retraso. Si quieres ejecuciones a las
:00, :05, :10, reserva la siguiente a partir de la reserva anterior, y sáltate las horas que ya han pasado:
Sub BookNext()
mNextRun = mNextRun + TimeSerial(0, INTERVAL_MIN, 0)
Do While mNextRun <= Now ' Excel estaba ocupado: saltar las horas perdidas
mNextRun = mNextRun + TimeSerial(0, INTERVAL_MIN, 0)
Loop
Application.OnTime mNextRun, "RefreshPrices"
End Sub
OnTime trabaja al segundo, no con más precisión. Para algo más rápido es la herramienta equivocada; la
guía de Timer trata cómo medir intervalos cortos.
La decisión de criterio: OnTime no es un programador de tareas
OnTime ejecuta una macro mientras este libro está abierto en el Excel de alguien. Es la herramienta
adecuada para un panel que se actualiza cada pocos minutos, un recordatorio de guardar o un mensaje de estado
que se borra solo. Es la herramienta equivocada para ejecutar la exportación cada noche a las 2 de la
madrugada: a esa hora no hay ningún Excel abierto, un portátil en suspensión se salta la hora, y un solo
restablecimiento o un cuelgue rompe la cadena sin dejar rastro.
Para tareas que tienen que ocurrir haya o no alguien delante del ordenador, usa el Programador de tareas de
Windows para abrir el libro y deja que Workbook_Open haga el trabajo, como describe la
guía de Workbook_Open. Reserva OnTime para medir el tiempo dentro de una
sesión, y dale a cada reserva una forma de cancelarse.
Cómo ayuda ExcelMaster
Los fallos de OnTime aparecen más tarde y en otro sitio: un archivo cerrado que se vuelve a abrir a
mediodía, una actualización que no hay manera de parar, un temporizador que solo se porta mal después de que
alguien pulsara Restablecer.
ExcelMaster lee el código del libro,
encuentra cada reserva de OnTime, comprueba que cada una tenga su cancelación correspondiente con la misma
hora guardada y puede añadir la limpieza en Workbook_BeforeClose que evita que el archivo se vuelva a abrir
solo.
Preguntas frecuentes
¿Cómo ejecuto una macro cada 5 minutos en Excel VBA?
Reserva la primera ejecución con Application.OnTime Now + TimeSerial(0, 5, 0), "MyMacro", y al final de
MyMacro reserva la siguiente de la misma forma. Guarda cada hora reservada en una variable de nivel de
módulo para poder cancelarla.
¿Cómo detengo Application.OnTime?
Llama a Application.OnTime con la misma hora y el mismo nombre de procedimiento que reservaste, y con
Schedule:=False. La hora tiene que coincidir exactamente, así que usa la variable guardada, no un nuevo
cálculo con Now.
¿Por qué OnTime da el error 1004 al cancelarlo?
Ninguna reserva coincide con la hora y el nombre de procedimiento que pasaste, normalmente porque la hora se
volvió a calcular o porque la reserva ya se ejecutó. Cancela con la hora guardada y envuelve la cancelación
en un On Error Resume Next de una sola línea.
¿Por qué mi libro se vuelve a abrir solo?
Una reserva hecha con OnTime seguía en la lista de Excel cuando se cerró el libro, así que Excel volvió a
abrir el archivo para ejecutar la macro. Cancela la reserva en Workbook_BeforeClose.
¿OnTime se ejecuta si Excel está cerrado?
No. La reserva solo existe en el Excel en ejecución. Para ejecutar una macro con Excel cerrado, programa el
libro con el Programador de tareas de Windows y pon el trabajo en Workbook_Open.
Probado en
Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 03/10/2026.
Guías relacionadas: VBA Shortcut Key · VBA SendKeys · VBA Wait · VBA Sleep · VBA Timer · VBA Now · VBA Global Variable · VBA Workbook_BeforeClose
