Color scheme: Dark
You are here: start » en » Documentation » Microsoft Access development tools » WKL Forms Object Library » Application and connections » WklTimerList

WklTimerList

WklTimerList runs scheduled tasks for an Access form: a search that starts 400 ms after the last key press, a counter refreshed every 30 seconds, a queue of web requests that keeps to the limit of a data service. One control holds any number of tasks, each with its own key, delay, interval and rules for overlaps, retries and time limits. It is visible in Design view and invisible in Form view.

Since release 1.60.0. The kinds wttEvent and wttHttp are available; wttSteps, wttFile, wttChecksum, wttZip and wttProgram are part of the contract but refused with wkeNotAvailable in this version.

Get started

Common tasks

  • Start a search 400 ms after the last key press: one task with Delay = 400, Restart in the Change event of the text box.
  • Refresh a counter every 30 seconds: one task with Interval = 30000, AutoStart = True.
  • Send 1 700 requests to a service that allows 300 per minute: one task of kind wttHttp with RateLimit = 300, RateWindow = 60000, one Requests.Add per row with the key of the row as Tag (How to: Send many requests to a web service at its pace).

Visual aid

graph TD
    A[Form opens] --> B{AutoStart}
    B -->|True| C[Tasks waiting]
    B -->|False| D[Tasks stopped until Start]
    D --> C
    C -->|due, wttEvent| E[TaskDue posted to the form]
    E -->|the procedure returned| C
    C -->|due, wttHttp| F[Queue sends its requests]
    F -->|each answer| R[RequestCompleted or RequestFailed]
    R --> F
    F -->|queue empty| G[TaskCompleted]
    G -->|Interval greater than 0| C
    X[Form closes] --> J[All tasks end, no event afterwards]

Key features

  • Several tasks per form, each identified by a key; started, stopped, paused and resumed one by one or all together.
  • First due time after a delay from the start, or at a clock time.
  • Repetition in a fixed interval that counts from the due time, not from the end of the run: a 2-second run does not shift a 30-second series.
  • Overlap rule for due times that pass while a run is still going: drop them (wtoSkip) or keep exactly one (wtoOnce). The queue never grows.
  • Retries and time limits per task.
  • Kinds of work: a VBA event (wttEvent), VBA in steps (wttSteps), and jobs the control runs itself in the background: web request (wttHttp), copy or move files (wttFile), checksum (wttChecksum), ZIP (wttZip), external program (wttProgram).
  • Paced web requests: a minimum gap, a limit per time window, a pause when the service answers 429, a halt of the whole queue on 401 until the application signs in again.

Core concepts

A timer does not make VBA parallel. VBA, forms, DAO and the Access object model run on the main thread of Access. A wttEvent or wttSteps task runs on that thread when it is due; only the background kinds run elsewhere, and their results come back to the main thread as events.

Events are posted, never raised from inside another event. VBA does not deliver an event of an object while an event procedure of the same object is running. The control therefore posts every due event to its own message window and delivers it after the running procedure has ended. A MsgBox in an event procedure delays the next event; it does not lose it.

A message window of its own. An ActiveX control that is invisible or disabled in Access has no window and receives no messages. The timer list uses a hidden message-only window that lives from the opening to the closing of the form, whatever Visible and Enabled say.

The life of a task ends with the form. When the form closes, every task stops, running background jobs are canceled and no event arrives afterwards.

The clock. Delays and intervals count on a monotonic counter, so a user who sets the clock of Windows forward or back does not make a task fire early or late. StartAt is a local clock time.

Rules

These rules decide when a task is due. They are the same for every kind of task.

SituationBehavior
Start with Delay = 0 and no StartAtDue at once.
StartAt in the futureDue at that clock time.
StartAt in the past, Interval = 0Due at once.
StartAt in the past, Interval > 0Due at the next time of the series StartAt + n × Interval after now.
Run completed, Interval > 0Next due time = previous due time + Interval.
Run completed, Interval = 0State wtsDone.
Due times passed during the run, wtoSkipThey are dropped; next due time is the next one of the series after now.
Due times passed during the run, wtoOnceOne run follows at once; the rest are dropped.
Trigger during a run, wtoOnceOne further run is noted; more triggers do not add more.
Trigger during a run, wtoSkipIgnored.
Run failed, Retries leftThe same run (same RunId) repeats after RetryDelay; TaskFailed with WillRetry = True.
Run failed, no retries leftTaskFailed with WillRetry = False; LastError keeps the text; the series goes on if Interval > 0.
Run canceledNever repeated.
Run takes longer than TimeoutBackground jobs: the attempt is canceled (for a web request queue: per request, see below). A wttEvent run is the VBA event procedure and cannot be interrupted; Timeout has no effect on it.
PauseThe time left is kept; Resume goes on with it.
Stop during a runThe run ends normally; nothing more is scheduled, no noted run follows.
RestartThe delay begins again from now: the delayed search after each key press.
Enabled = FalseNever due, also not on Trigger.

Rules for web requests (wttHttp). A task of this kind is a queue: it holds any number of requests, sends them one after another and keeps the pace below for all of them together.

SituationBehavior
MinGapThe least time between two request starts of the queue.
RateLimit, RateWindowAt most RateLimit starts in any RateWindow milliseconds. Both can be set in any order; Start of a queue with a RateLimit but no RateWindow is refused with wkeInvalidValue.
Answer 429 with Retry-AfterThe whole queue pauses for that time (seconds or an HTTP date, RFC 9110, section 10.2.3); a longer pause already set stays. QueuePaused reports reason and end. The request is sent again.
Answer 429 without Retry-AfterAs above, for RateWindow milliseconds; without a RateWindow, for 60 seconds.
Answer 401The whole queue halts; only QueueHalted reports it, with the status and the response headers. The application signs in again on the main thread, sets the shared headers and calls Resume; the halted request is sent again first.
A second 401 in a row for the same requestRequestFailed for it (so a failed sign-in does not loop), and the queue halts again with QueueHalted.
No answer (connection broken, time limit)Sent again after RetryDelay while Retries are left, before every other waiting request.
Timeout, Retries, RetryDelayApply per request and per attempt, not to the whole queue.
Any other answer, also 5xxReported with RequestFailed, never sent again: a repetition could do the work twice.
A repetitionSends the same own headers, the same body and the same Idempotency-Key. Shared headers are read at every send, so a new session header after a 401 applies to it and to every request still waiting.
StopNothing more is sent. The request on the way is not canceled: its answer comes and is reported, because a closed connection can leave a change at the service whose outcome nobody knows. The requests not yet sent stay with state wrsWaiting, without an event. Start sends them.
Requests.Clear or Request.CancelEach request not yet done ends with RequestCancelled and state wrsCancelled; a request on the way has its connection closed and is never sent again. Its outcome at the service is unknown: send it again yourself with the same IdempotencyKey if the service supports it.
Several queues against the same serviceDo not share their pace: each keeps its own MinGap and RateLimit. Put all requests to one service in one queue, for example on the shell form.
The queue is emptyTaskCompleted with Result = Array(completed, failed), the numbers of requests completed and failed for good in this run. New requests added later start the queue again while the task is started.

Encoding, sign-in and secrets:

TopicRule
Request bodySent as UTF-8 with the ContentType of the request (default application/json; charset=utf-8).
Response textResult.Text is decoded by the charset of the response; without one, as UTF-8.
Windows sign-inUseWindowsLogin = True signs in with the Windows account of the Access process (Negotiate, NTLM) when the service asks for it. Default False.
SecretsShared headers, own headers, request and response bodies stay in memory only. The trace and error texts name the method, the address without its query and the status; never a header value or a body.

Contract

WklTimerList (control) with the collection Tasks of WklTimerTask.

Member of WklTimerTaskMeaning
KeyUnique text, compared without regard to case.
EnabledThe task takes part.
KindwttEvent, wttSteps, wttHttp, wttFile, wttChecksum, wttZip, wttProgram.
Delay, StartAtFirst due time: milliseconds after Start, or a clock time.
IntervalRepetition in milliseconds; 0 = once.
OverlapwtoSkip or wtoOnce.
Timeout, Retries, RetryDelayTime limit of a run (0 = none), retries after a failure, wait before a retry.
TagAny value; passed unchanged to every event.
StatewtsStopped, wtsWaiting, wtsRunning, wtsPaused, wtsDone (read only).
RunCount, LastRun, NextRun, LastErrorRead only.
Start, Stop, Pause, Resume, Trigger, RestartSee Rules.
Member of WklTimerListMeaning
TasksThe tasks; Add(Key), Remove(Key), Item(Key), Count.
Start, Stop, Pause, ResumeFor all tasks.
AutoStartStart all tasks when the form opens.
RunningThe number of runs going on.
MaxParallelBackground jobs at the same time; default 1. Reserved for the file, checksum, ZIP and program kinds; each web request queue always sends one request at a time, and several queues run side by side.
EventWhen
TaskDue(Key, RunId, Tag, Cancel)A wttEvent run is due; the event procedure is the run. Cancel = True skips it; the series goes on. A web request queue raises TaskStarted instead, with its first request.
TaskStep(Key, RunId, Step, Done, Progress)wttSteps only: once per step; Access gets control back between steps.
TaskStarted(Key, RunId)A background job started.
TaskProgress(Key, RunId, Percent, Text)A background job reports progress.
TaskCompleted(Key, RunId, Result)A background run ended successfully; for a web request queue, the queue became empty. A wttEvent run raises none: its run is TaskDue.
TaskFailed(Key, RunId, Number, Description, WillRetry)A run failed.
TaskCancelled(Key, RunId, Reason)A run was canceled (by Stop of a background job, by the time limit, by closing the form).

Result by kind: files and ZIP Files, Bytes; checksum Hash; program ExitCode, Output, ErrorOutput.

Web request queue

Members of a WklTimerTask of kind wttHttp:

MemberMeaning
RequestsThe requests of the queue; Add(Method, Url, [Body], [Tag], [ContentType]) As WklHttpRequest, Item(Id), Count (done ones too), Waiting (not yet sent), Clear, Forget (removes done requests, returns how many).
SharedHeader(Name)Read and write a header sent with every request of the queue, for example X-Api-Key, Authorization, the session header of a second factor. An empty value removes it.
MinGap, RateLimit, RateWindowThe pace, see Rules.
UseWindowsLoginSign in with the Windows account, see above.
Halted, PausedUntilRead only: the queue waits for Resume after a 401; the end of a pause after 429, or Null.

Members of WklHttpRequest:

MemberMeaning
IdNumber of the request, unique in the task; passed to every request event.
Method, Url, Body, ContentTypeAs given to Add; changeable until the request is sent.
Header(Name)An own header of this request.
IdempotencyKeySent as Idempotency-Key; a new GUID by default, the same for every repetition. An empty value sends none.
TagAny value; passed unchanged to every request event, for example the key of a row.
StatewrsWaiting, wrsSending, wrsCompleted, wrsFailed, wrsCancelled.
CancelEnds the request: not yet sent, it is taken out of the queue; on the way, the connection is closed.

Events of the queue (in addition to the task events above):

EventWhen
RequestCompleted(Key, Id, Tag, Status, Result)An answer 2xx or 3xx arrived. Result: Status, Text, Header(Name), Headers. More requests may be added here.
RequestFailed(Key, Id, Tag, Status, Description, WillRetry, Result)Another answer, or no answer; Status 0 when none came. Result holds the answer (Text, Header(Name)) when one came, for example a problem+json message; Nothing otherwise.
RequestCancelled(Key, Id, Tag, Reason)By Cancel, Requests.Clear, the time limit or the closing of the form.
QueuePaused(Key, Reason, ResumeAt)A 429 paused the queue until ResumeAt (local time).
QueueHalted(Key, Id, Status, Headers)A 401 halted the queue; Headers holds the response headers as text.
QueueResumed(Key)The pause ended or Resume was called.

Administration

Troubleshooting

None yet.

For developers

  • WklApplication: the application as a whole, where a shell form with its timers lives.
en/wiki/msaccess/comctl/application-and-connections/timer-list/start.txt · Last modified: by Wolfgang van der Stille